This is an automated email from the ASF dual-hosted git repository.

lxy-9602 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 0078e187 refactor(parquet): generalize read type adaptation for BLOB 
(#322)
0078e187 is described below

commit 0078e18794e04b548c6e72872ac893da6c2ea7e0
Author: Zhang Jiawei <[email protected]>
AuthorDate: Fri Sep 11 14:11:49 2026 +0800

    refactor(parquet): generalize read type adaptation for BLOB (#322)
---
 src/paimon/core/schema/arrow_schema_validator.cpp  |   8 +-
 .../core/schema/arrow_schema_validator_test.cpp    |  17 ++--
 src/paimon/core/schema/schema_validation.cpp       |   6 +-
 src/paimon/core/schema/schema_validation_test.cpp  |   3 +-
 src/paimon/core/table/format/format_table_read.cpp |   2 +-
 src/paimon/format/parquet/CMakeLists.txt           |   4 +-
 .../format/parquet/parquet_file_batch_reader.cpp   |  39 +++----
 .../format/parquet/parquet_file_batch_reader.h     |   7 +-
 ...converter.cpp => parquet_read_type_adapter.cpp} | 113 ++++++++++++---------
 ...amp_converter.h => parquet_read_type_adapter.h} |  24 +++--
 ...test.cpp => parquet_read_type_adapter_test.cpp} |  69 ++++++++-----
 test/inte/paimon_read_compat_inte_test.cpp         |  26 ++++-
 .../append_types_compatibility.db/README.md        |  18 +++-
 ...8a49858f-c0a7-468e-8d71-45b65142d422-0.parquet} | Bin
 ...ta-8a49858f-c0a7-468e-8d71-45b65142d422-1.blob} | Bin
 ...anifest-4db45a0c-f933-4043-80de-f1d5a5107966-0} | Bin 2249 -> 2232 bytes
 ...st-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-0} | Bin 1006 -> 1006 bytes
 ...st-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-1} | Bin 1006 -> 1115 bytes
 .../schema/schema-0                                |  14 +--
 .../snapshot/EARLIEST                              |   0
 .../snapshot/LATEST                                |   0
 .../java_array_blob_types/snapshot/snapshot-1      |  18 ++++
 ...d2b0d467-f565-4453-b400-468f9ec22a54-0.parquet} | Bin
 ...ta-d2b0d467-f565-4453-b400-468f9ec22a54-1.blob} | Bin
 ...anifest-62c45eba-0a54-4a63-b89d-0ab09a768412-0} | Bin 2249 -> 2233 bytes
 ...st-list-d3262408-2da8-408b-9c67-b487ecf8565b-0} | Bin 1111 -> 1006 bytes
 ...st-list-d3262408-2da8-408b-9c67-b487ecf8565b-1} | Bin 1006 -> 1115 bytes
 .../schema/schema-0                                |  13 +--
 .../snapshot/EARLIEST                              |   0
 .../snapshot/LATEST                                |   0
 .../java_map_blob_types/snapshot/snapshot-1        |  18 ++++
 .../java_nested_blob_types/snapshot/snapshot-1     |  18 ----
 ...ta-043fd246-38df-40f7-9147-c1c416282ecc-0.blob} | Bin
 ...3cb0cb1a-5db9-478e-a727-6f34d6e800e5-0.parquet} | Bin
 ...anifest-8e8abfa0-a7fe-4669-a7be-cd095dee5fac-0} | Bin 2333 -> 2296 bytes
 ...st-list-2f17c472-fea6-42d0-8300-12da4874d53a-0} | Bin 799 -> 799 bytes
 ...st-list-2f17c472-fea6-42d0-8300-12da4874d53a-1} | Bin 899 -> 899 bytes
 .../schema/schema-0                                |  18 +---
 .../snapshot/LATEST                                |   0
 .../python_array_blob_types/snapshot/snapshot-1    |  15 +++
 ...4575b349-773b-4e2f-b364-ad98ee38fa72-0.parquet} | Bin
 ...ta-6e33a029-ca69-4453-a05b-2f7ed1a93f63-0.blob} | Bin
 ...anifest-59ba3981-44aa-4af6-960e-9048ce6e883b-0} | Bin 2333 -> 2298 bytes
 ...st-list-c762398d-e22c-4676-b43d-4564909a0859-0} | Bin 799 -> 799 bytes
 ...st-list-c762398d-e22c-4676-b43d-4564909a0859-1} | Bin 799 -> 899 bytes
 .../schema/schema-0                                |  17 +---
 .../snapshot/LATEST                                |   0
 .../python_map_blob_types/snapshot/snapshot-1      |  15 +++
 .../python_nested_blob_types/snapshot/snapshot-1   |  15 ---
 ...-334e3ff1-32c3-4f59-98c1-7e366b0f4b0b-0.parquet | Bin 0 -> 1124 bytes
 ...manifest-37d9c141-c626-434c-bdc0-506cc6f3d04e-0 | Bin 0 -> 2421 bytes
 ...est-list-edec5851-5eca-466b-9bde-780df761f932-0 | Bin 0 -> 1117 bytes
 ...st-list-edec5851-5eca-466b-9bde-780df761f932-1} | Bin 1117 -> 1219 bytes
 .../rust_array_blob_types/schema/schema-0          |   1 +
 .../snapshot/LATEST                                |   0
 .../rust_array_blob_types/snapshot/snapshot-1      |   1 +
 ...-27af7a11-39ca-4131-b12d-81f824f57978-0.parquet | Bin 0 -> 1503 bytes
 ...manifest-30836888-cc9d-4ad0-b200-6da6e101ebd4-0 | Bin 0 -> 2418 bytes
 ...est-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-0 | Bin 0 -> 1117 bytes
 ...st-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-1} | Bin 1219 -> 1219 bytes
 .../rust_map_blob_types/schema/schema-0            |   1 +
 .../snapshot/LATEST                                |   0
 .../rust_map_blob_types/snapshot/snapshot-1        |   1 +
 ...-4749ab27-cf5e-48e5-8449-3f674480e077-0.parquet | Bin 2015 -> 0 bytes
 ...manifest-0d1ac1c3-300f-45fe-ad80-45445dec4b45-0 | Bin 2432 -> 0 bytes
 .../rust_nested_blob_types/schema/schema-0         |   1 -
 .../rust_nested_blob_types/snapshot/snapshot-1     |   1 -
 67 files changed, 291 insertions(+), 212 deletions(-)

diff --git a/src/paimon/core/schema/arrow_schema_validator.cpp 
b/src/paimon/core/schema/arrow_schema_validator.cpp
index 00997936..c008d222 100644
--- a/src/paimon/core/schema/arrow_schema_validator.cpp
+++ b/src/paimon/core/schema/arrow_schema_validator.cpp
@@ -173,7 +173,9 @@ Status ArrowSchemaValidator::ValidateDataTypeWithFieldId(
         case arrow::Type::type::LARGE_BINARY: {
             if (BlobUtils::IsBlobMetadata(key_value_metadata)) {
                 if (!allow_blob) {
-                    return Status::Invalid("Blob field must be a top-level 
field.");
+                    return Status::Invalid(
+                        "BLOB field must be a top-level field or the direct 
value of a "
+                        "top-level MAP field.");
                 }
                 break;
             }
@@ -258,7 +260,9 @@ Status ArrowSchemaValidator::ValidateField(const 
std::shared_ptr<arrow::Field>&
         case arrow::Type::type::LARGE_BINARY: {
             if (BlobUtils::IsBlobField(field)) {
                 if (!allow_blob) {
-                    return Status::Invalid("Blob field must be a top-level 
field.");
+                    return Status::Invalid(
+                        "BLOB field must be a top-level field or the direct 
value of a "
+                        "top-level MAP field.");
                 }
                 break;
             }
diff --git a/src/paimon/core/schema/arrow_schema_validator_test.cpp 
b/src/paimon/core/schema/arrow_schema_validator_test.cpp
index d644e563..283b61ec 100644
--- a/src/paimon/core/schema/arrow_schema_validator_test.cpp
+++ b/src/paimon/core/schema/arrow_schema_validator_test.cpp
@@ -170,7 +170,7 @@ TEST(ArrowSchemaValidatorTest, TestInvalidDataType) {
     }
 }
 
-TEST(ArrowSchemaValidatorTest, TestBlobFieldMustBeTopLevel) {
+TEST(ArrowSchemaValidatorTest, TestBlobFieldPlacement) {
     {
         auto arrow_schema =
             arrow::schema(arrow::FieldVector({BlobUtils::ToArrowField("blob", 
true)}));
@@ -186,14 +186,16 @@ TEST(ArrowSchemaValidatorTest, 
TestBlobFieldMustBeTopLevel) {
             arrow::field("nested", 
arrow::struct_({BlobUtils::ToArrowField("blob", true)}));
         auto arrow_schema = 
arrow::schema(arrow::FieldVector({nested_blob_field}));
         
ASSERT_NOK_WITH_MSG(ArrowSchemaValidator::ValidateSchema(*arrow_schema),
-                            "Blob field must be a top-level field.");
+                            "BLOB field must be a top-level field or the 
direct value of a "
+                            "top-level MAP field.");
     }
     {
         auto array_blob_field =
             arrow::field("array_blob", 
arrow::list(BlobUtils::ToArrowField("item", true)));
         auto arrow_schema = 
arrow::schema(arrow::FieldVector({array_blob_field}));
         
ASSERT_NOK_WITH_MSG(ArrowSchemaValidator::ValidateSchema(*arrow_schema),
-                            "Blob field must be a top-level field.");
+                            "BLOB field must be a top-level field or the 
direct value of a "
+                            "top-level MAP field.");
     }
     {
         auto map_blob_field = arrow::field(
@@ -211,7 +213,8 @@ TEST(ArrowSchemaValidatorTest, TestBlobFieldMustBeTopLevel) 
{
             arrow::map(arrow::utf8(), 
arrow::struct_({BlobUtils::ToArrowField("blob", true)})));
         auto arrow_schema = 
arrow::schema(arrow::FieldVector({map_blob_field}));
         
ASSERT_NOK_WITH_MSG(ArrowSchemaValidator::ValidateSchema(*arrow_schema),
-                            "Blob field must be a top-level field.");
+                            "BLOB field must be a top-level field or the 
direct value of a "
+                            "top-level MAP field.");
     }
     {
         auto nested_map_blob_field = arrow::field(
@@ -220,7 +223,8 @@ TEST(ArrowSchemaValidatorTest, TestBlobFieldMustBeTopLevel) 
{
                        arrow::map(arrow::utf8(), 
BlobUtils::ToArrowField("value", true))));
         auto arrow_schema = 
arrow::schema(arrow::FieldVector({nested_map_blob_field}));
         
ASSERT_NOK_WITH_MSG(ArrowSchemaValidator::ValidateSchema(*arrow_schema),
-                            "Blob field must be a top-level field.");
+                            "BLOB field must be a top-level field or the 
direct value of a "
+                            "top-level MAP field.");
     }
     {
         std::vector<DataField> nested_fields = {
@@ -230,7 +234,8 @@ TEST(ArrowSchemaValidatorTest, TestBlobFieldMustBeTopLevel) 
{
             arrow::field("nested", 
DataField::ConvertDataFieldsToArrowStructType(nested_fields)))};
         auto arrow_schema = DataField::ConvertDataFieldsToArrowSchema(fields);
         
ASSERT_NOK_WITH_MSG(ArrowSchemaValidator::ValidateSchemaWithFieldId(*arrow_schema),
-                            "Blob field must be a top-level field.");
+                            "BLOB field must be a top-level field or the 
direct value of a "
+                            "top-level MAP field.");
     }
 }
 
diff --git a/src/paimon/core/schema/schema_validation.cpp 
b/src/paimon/core/schema/schema_validation.cpp
index 19fbdb13..0dd2533d 100644
--- a/src/paimon/core/schema/schema_validation.cpp
+++ b/src/paimon/core/schema/schema_validation.cpp
@@ -789,9 +789,9 @@ Status SchemaValidation::ValidateMosaicDataFields(const 
TableSchema& schema,
     const std::vector<std::string> inline_blob_fields = 
options.GetBlobInlineFields();
     const std::set<std::string> 
inline_blob_field_set(inline_blob_fields.begin(),
                                                       
inline_blob_fields.end());
-    // Match Java SchemaValidation by validating only fields stored in the 
normal data file. C++
-    // permits BLOB only as a top-level field; descriptor and view fields are 
inline, so Mosaic
-    // must reject them here.
+    // Match Java SchemaValidation by validating only fields stored in the 
normal data file.
+    // Top-level BLOB fields stored in separate files are skipped; descriptor 
and view fields are
+    // inline, so Mosaic must reject them here.
     for (const DataField& field : schema.Fields()) {
         if (BlobUtils::IsBlobField(field.ArrowField()) &&
             inline_blob_field_set.count(field.Name()) == 0) {
diff --git a/src/paimon/core/schema/schema_validation_test.cpp 
b/src/paimon/core/schema/schema_validation_test.cpp
index fed7ea5a..11755578 100644
--- a/src/paimon/core/schema/schema_validation_test.cpp
+++ b/src/paimon/core/schema/schema_validation_test.cpp
@@ -1245,7 +1245,8 @@ TEST(SchemaValidationTest, 
TestMapSharedShreddingRejectsBlobValue) {
     });
     ASSERT_NOK_WITH_MSG(TableSchema::Create(/*schema_id=*/0, nested_schema,
                                             /*partition_keys=*/{}, 
/*primary_keys=*/{}, options),
-                        "Blob field must be a top-level field.");
+                        "BLOB field must be a top-level field or the direct 
value of a "
+                        "top-level MAP field.");
 }
 
 TEST(SchemaValidationTest, TestMapSharedShreddingCompression) {
diff --git a/src/paimon/core/table/format/format_table_read.cpp 
b/src/paimon/core/table/format/format_table_read.cpp
index cda8cbfe..7be0e2f8 100644
--- a/src/paimon/core/table/format/format_table_read.cpp
+++ b/src/paimon/core/table/format/format_table_read.cpp
@@ -60,7 +60,7 @@ namespace {
 /// `table_unit`. A file's unit means something else in each format, so the 
rule does too:
 ///
 /// - parquet records the unit, so the file's is the one its values are in. 
Only `MILLI` under a
-///   `SECOND` column is rescaled, by `ParquetTimestampConverter`, which 
parquet's own round trip
+///   `SECOND` column is rescaled, by `ParquetReadTypeAdapter`, which 
parquet's own round trip
 ///   needs since it has no `SECOND`. Any other pair would be relabelled 
rather than rescaled -
 ///   `us` under an `ms` type reads a thousand times too large - so it is 
refused;
 /// - orc records none: a value is a seconds-plus-nanoseconds pair that 
`OrcAdapter` builds at
diff --git a/src/paimon/format/parquet/CMakeLists.txt 
b/src/paimon/format/parquet/CMakeLists.txt
index 60d89851..937e7124 100644
--- a/src/paimon/format/parquet/CMakeLists.txt
+++ b/src/paimon/format/parquet/CMakeLists.txt
@@ -19,7 +19,7 @@ set(PAIMON_PARQUET_FILE_FORMAT
     predicate_converter.cpp
     file_reader_wrapper.cpp
     page_filtered_row_group_reader.cpp
-    parquet_timestamp_converter.cpp
+    parquet_read_type_adapter.cpp
     parquet_file_batch_reader.cpp
     parquet_file_format_factory.cpp
     parquet_format_writer.cpp
@@ -54,7 +54,7 @@ if(PAIMON_BUILD_TESTS)
                     SOURCES
                     file_reader_wrapper_test.cpp
                     page_filtered_row_group_reader_test.cpp
-                    parquet_timestamp_converter_test.cpp
+                    parquet_read_type_adapter_test.cpp
                     parquet_vector_io_test.cpp
                     parquet_field_id_converter_test.cpp
                     parquet_file_batch_reader_test.cpp
diff --git a/src/paimon/format/parquet/parquet_file_batch_reader.cpp 
b/src/paimon/format/parquet/parquet_file_batch_reader.cpp
index 89758dc5..76f0df13 100644
--- a/src/paimon/format/parquet/parquet_file_batch_reader.cpp
+++ b/src/paimon/format/parquet/parquet_file_batch_reader.cpp
@@ -54,8 +54,8 @@
 #include "paimon/core/utils/nested_projection_utils.h"
 #include "paimon/format/parquet/parquet_field_id_converter.h"
 #include "paimon/format/parquet/parquet_format_defs.h"
+#include "paimon/format/parquet/parquet_read_type_adapter.h"
 #include "paimon/format/parquet/parquet_schema_util.h"
-#include "paimon/format/parquet/parquet_timestamp_converter.h"
 #include "paimon/format/parquet/predicate_converter.h"
 #include "paimon/reader/batch_reader.h"
 #include "paimon/utils/roaring_bitmap32.h"
@@ -75,10 +75,9 @@ class Predicate;
 namespace paimon::parquet {
 
 namespace {
-// LIST/MAP do not support pruning fields from their nested value types, but 
physical and
-// logical leaf types may still differ (for example, Parquet reports LTZ 
timestamps as UTC
-// while Paimon exposes them in the local timezone). Compare only the nested 
projection shape
-// here so those representation differences are handled by the normal cast 
path.
+// LIST, MAP, and FIXED_SIZE_LIST do not support partial projection of their 
nested values.
+// Require matching nested structures while allowing the leaf-type 
representation differences
+// explicitly handled below.
 bool HasSameNestedProjectionShape(const std::shared_ptr<arrow::DataType>& 
read_type,
                                   const std::shared_ptr<arrow::DataType>& 
file_type) {
     const bool read_is_nested = ArrowSchemaValidator::IsNestedType(read_type);
@@ -87,8 +86,8 @@ bool HasSameNestedProjectionShape(const 
std::shared_ptr<arrow::DataType>& read_t
         if (read_is_nested || file_is_nested) {
             return false;
         }
-        // ParquetTimestampConverter explicitly supports timestamp unit and 
timezone
-        // conversion after reading. Other atomic type differences remain 
unsupported here.
+        // ParquetReadTypeAdapter supports timezone retyping and the 
millisecond-to-second
+        // conversion required for Paimon timestamps after reading.
         if (read_type->id() == arrow::Type::TIMESTAMP &&
             file_type->id() == arrow::Type::TIMESTAMP) {
             const auto& read_timestamp = static_cast<const 
arrow::TimestampType&>(*read_type);
@@ -356,11 +355,11 @@ Result<std::unique_ptr<::ArrowSchema>> 
ParquetFileBatchReader::GetFileSchema() c
         PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Schema> new_schema,
                                
ParquetFieldIdConverter::GetPaimonIdsFromParquetIds(file_schema));
         PAIMON_ASSIGN_OR_RAISE(
-            std::shared_ptr<arrow::DataType> new_type,
-            
ParquetTimestampConverter::AdjustTimezone(arrow::struct_(new_schema->fields())));
+            std::shared_ptr<arrow::DataType> normalized_type,
+            
ParquetReadTypeAdapter::NormalizeFileType(arrow::struct_(new_schema->fields())));
 
         auto c_schema = std::make_unique<::ArrowSchema>();
-        PAIMON_RETURN_NOT_OK_FROM_ARROW(arrow::ExportType(*new_type, 
c_schema.get()));
+        PAIMON_RETURN_NOT_OK_FROM_ARROW(arrow::ExportType(*normalized_type, 
c_schema.get()));
         return c_schema;
     }
     
PAIMON_PARQUET_CATCH_AND_RETURN_STATUS("ParquetFileBatchReader::GetFileSchema")
@@ -378,8 +377,8 @@ Status ParquetFileBatchReader::SetReadSchema(
 
         PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Schema> file_schema, 
GetLogicalFileSchema());
 
-        // Recursively match read_schema against file_schema by field names.
-        // STRUCT supports sub-field projection; LIST/MAP require exact type 
match.
+        // Recursively match read_schema against file_schema by field names. 
STRUCT supports
+        // sub-field projection; LIST, MAP, and FIXED_SIZE_LIST require 
matching nested shapes.
         PAIMON_ASSIGN_OR_RAISE(std::vector<int32_t> column_indices,
                                ComputeNestedColumnIndices(read_schema, 
file_schema));
 
@@ -773,20 +772,8 @@ Result<BatchReader::ReadBatch> 
ParquetFileBatchReader::NextBatch() {
         PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Array> array,
                                           batch->ToStructArray());
         PAIMON_RETURN_NOT_OK_FROM_ARROW(array->Validate());
-        PAIMON_ASSIGN_OR_RAISE(bool need_cast, 
ParquetTimestampConverter::NeedCastArrayForTimestamp(
-                                                   array->type(), 
read_data_type_));
-        if (need_cast) {
-            PAIMON_ASSIGN_OR_RAISE(array, 
ParquetTimestampConverter::CastArrayForTimestamp(
-                                              array, read_data_type_, 
arrow_pool_));
-        }
-        PAIMON_ASSIGN_OR_RAISE(need_cast, 
ParquetTimestampConverter::NeedCastArrayForTimestamp(
-                                              array->type(), read_data_type_));
-        if (need_cast) {
-            return Status::Invalid(fmt::format(
-                "unexpected: in parquet, after CastArrayForTimestamp, output 
type {} not "
-                "equal with read schema {}",
-                array->type()->ToString(), read_data_type_->ToString()));
-        }
+        PAIMON_ASSIGN_OR_RAISE(
+            array, ParquetReadTypeAdapter::AdaptArray(array, read_data_type_, 
arrow_pool_));
         PAIMON_RETURN_NOT_OK(GenerateRowMapping(array->length()));
         std::unique_ptr<ArrowArray> c_array = std::make_unique<ArrowArray>();
         std::unique_ptr<ArrowSchema> c_schema = 
std::make_unique<ArrowSchema>();
diff --git a/src/paimon/format/parquet/parquet_file_batch_reader.h 
b/src/paimon/format/parquet/parquet_file_batch_reader.h
index 95fcf2be..565dec9b 100644
--- a/src/paimon/format/parquet/parquet_file_batch_reader.h
+++ b/src/paimon/format/parquet/parquet_file_batch_reader.h
@@ -201,8 +201,8 @@ class ParquetFileBatchReader : public 
PrefetchFileBatchReader {
 
     /// Recursively collect leaf column indices for the sub-fields in read_type
     /// that match file_type by paimon field ID. Unmatched sub-fields in 
file_type
-    /// have their leaf indices skipped. Partial projection inside LIST/MAP is
-    /// not supported and will return Invalid.
+    /// have their leaf indices skipped. Partial projection inside LIST, MAP, 
or
+    /// FIXED_SIZE_LIST is not supported and will return Invalid.
     static Status CollectLeafIndices(const std::shared_ptr<arrow::DataType>& 
read_type,
                                      const std::shared_ptr<arrow::DataType>& 
file_type,
                                      int32_t* leaf_index, 
std::vector<int32_t>* indices);
@@ -213,7 +213,8 @@ class ParquetFileBatchReader : public 
PrefetchFileBatchReader {
 
     /// Compute leaf column indices by recursively matching read_schema against
     /// file_schema using paimon field IDs. STRUCT supports sub-field 
projection
-    /// (unmatched sub-fields are skipped). LIST/MAP require exact type match.
+    /// (unmatched sub-fields are skipped). LIST, MAP, and FIXED_SIZE_LIST 
require
+    /// matching nested shapes.
     static Result<std::vector<int32_t>> ComputeNestedColumnIndices(
         const std::shared_ptr<arrow::Schema>& read_schema,
         const std::shared_ptr<arrow::Schema>& file_schema);
diff --git a/src/paimon/format/parquet/parquet_timestamp_converter.cpp 
b/src/paimon/format/parquet/parquet_read_type_adapter.cpp
similarity index 68%
rename from src/paimon/format/parquet/parquet_timestamp_converter.cpp
rename to src/paimon/format/parquet/parquet_read_type_adapter.cpp
index 01521762..8ba47dc0 100644
--- a/src/paimon/format/parquet/parquet_timestamp_converter.cpp
+++ b/src/paimon/format/parquet/parquet_read_type_adapter.cpp
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-#include "paimon/format/parquet/parquet_timestamp_converter.h"
+#include "paimon/format/parquet/parquet_read_type_adapter.h"
 
 #include <memory>
 #include <string>
@@ -31,10 +31,9 @@
 #include "paimon/core/casting/timestamp_to_timestamp_cast_executor.h"
 
 namespace paimon::parquet {
-namespace {
 
-bool IsAlreadyLogicalBlob(const std::shared_ptr<arrow::Field>& src_field,
-                          const std::shared_ptr<arrow::Field>& target_field) {
+bool ParquetReadTypeAdapter::IsCompatibleBlob(const 
std::shared_ptr<arrow::Field>& src_field,
+                                              const 
std::shared_ptr<arrow::Field>& target_field) {
     // ARROW:schema may restore an inline BLOB's physical binary column to its 
logical
     // large_binary representation before this compatibility check runs.
     return src_field->type()->id() == arrow::Type::LARGE_BINARY &&
@@ -42,9 +41,7 @@ bool IsAlreadyLogicalBlob(const 
std::shared_ptr<arrow::Field>& src_field,
            BlobUtils::IsBlobMetadata(target_field->metadata());
 }
 
-}  // namespace
-
-Result<std::shared_ptr<arrow::DataType>> 
ParquetTimestampConverter::AdjustTimezone(
+Result<std::shared_ptr<arrow::DataType>> 
ParquetReadTypeAdapter::NormalizeFileType(
     const std::shared_ptr<arrow::DataType>& src_data_type) {
     arrow::Type::type type = src_data_type->id();
     switch (type) {
@@ -54,7 +51,7 @@ Result<std::shared_ptr<arrow::DataType>> 
ParquetTimestampConverter::AdjustTimezo
             new_fields.reserve(src_struct_type->num_fields());
             for (int32_t i = 0; i < src_struct_type->num_fields(); ++i) {
                 PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::DataType> 
sub_type,
-                                       
AdjustTimezone(src_struct_type->field(i)->type()));
+                                       
NormalizeFileType(src_struct_type->field(i)->type()));
                 
new_fields.push_back(src_struct_type->field(i)->WithType(sub_type));
             }
             return arrow::struct_(new_fields);
@@ -62,9 +59,9 @@ Result<std::shared_ptr<arrow::DataType>> 
ParquetTimestampConverter::AdjustTimezo
         case arrow::Type::type::MAP: {
             auto* src_map_type = 
checked_cast<arrow::MapType*>(src_data_type.get());
             PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::DataType> key_type,
-                                   AdjustTimezone(src_map_type->key_type()));
+                                   
NormalizeFileType(src_map_type->key_type()));
             PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::DataType> item_type,
-                                   AdjustTimezone(src_map_type->item_type()));
+                                   
NormalizeFileType(src_map_type->item_type()));
             return std::make_shared<arrow::MapType>(
                 src_map_type->key_field()->WithType(key_type),
                 src_map_type->item_field()->WithType(item_type));
@@ -72,7 +69,7 @@ Result<std::shared_ptr<arrow::DataType>> 
ParquetTimestampConverter::AdjustTimezo
         case arrow::Type::type::LIST: {
             auto* src_list_type = 
checked_cast<arrow::ListType*>(src_data_type.get());
             PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::DataType> value_type,
-                                   
AdjustTimezone(src_list_type->value_type()));
+                                   
NormalizeFileType(src_list_type->value_type()));
             return 
arrow::list(src_list_type->value_field()->WithType(value_type));
         }
         case arrow::Type::type::TIMESTAMP: {
@@ -86,12 +83,25 @@ Result<std::shared_ptr<arrow::DataType>> 
ParquetTimestampConverter::AdjustTimezo
     }
 }
 
-Result<bool> ParquetTimestampConverter::NeedCastArrayForTimestamp(
+Result<bool> ParquetReadTypeAdapter::NeedsArrayConversion(
     const std::shared_ptr<arrow::DataType>& src_data_type,
     const std::shared_ptr<arrow::DataType>& target_data_type) {
+    return NeedsArrayConversionImpl(arrow::field("", src_data_type),
+                                    arrow::field("", target_data_type));
+}
+
+Result<bool> ParquetReadTypeAdapter::NeedsArrayConversionImpl(
+    const std::shared_ptr<arrow::Field>& src_field,
+    const std::shared_ptr<arrow::Field>& target_field) {
+    if (IsCompatibleBlob(src_field, target_field)) {
+        return false;
+    }
+
+    const std::shared_ptr<arrow::DataType>& src_data_type = src_field->type();
+    const std::shared_ptr<arrow::DataType>& target_data_type = 
target_field->type();
     arrow::Type::type type = src_data_type->id();
     if (type != target_data_type->id()) {
-        return Status::Invalid(fmt::format("src type {} and target type {} 
mismatch",
+        return Status::Invalid(fmt::format("source type {} and target type {} 
mismatch",
                                            src_data_type->ToString(),
                                            target_data_type->ToString()));
     }
@@ -101,18 +111,13 @@ Result<bool> 
ParquetTimestampConverter::NeedCastArrayForTimestamp(
             auto* target_struct_type = 
checked_cast<arrow::StructType*>(target_data_type.get());
             if (src_struct_type->num_fields() != 
target_struct_type->num_fields()) {
                 return Status::Invalid(
-                    fmt::format("src type {} and target type {} number of 
fields mismatch",
+                    fmt::format("source type {} and target type {} number of 
fields mismatch",
                                 src_data_type->ToString(), 
target_data_type->ToString()));
             }
             for (int32_t i = 0; i < src_struct_type->num_fields(); ++i) {
-                const std::shared_ptr<arrow::Field>& src_field = 
src_struct_type->field(i);
-                const std::shared_ptr<arrow::Field>& target_field = 
target_struct_type->field(i);
-                if (IsAlreadyLogicalBlob(src_field, target_field)) {
-                    continue;
-                }
-                PAIMON_ASSIGN_OR_RAISE(
-                    bool need_cast,
-                    NeedCastArrayForTimestamp(src_field->type(), 
target_field->type()));
+                PAIMON_ASSIGN_OR_RAISE(bool need_cast,
+                                       
NeedsArrayConversionImpl(src_struct_type->field(i),
+                                                                
target_struct_type->field(i)));
                 if (need_cast) {
                     return true;
                 }
@@ -124,38 +129,54 @@ Result<bool> 
ParquetTimestampConverter::NeedCastArrayForTimestamp(
             auto* target_map_type = 
checked_cast<arrow::MapType*>(target_data_type.get());
             PAIMON_ASSIGN_OR_RAISE(
                 bool need_cast,
-                NeedCastArrayForTimestamp(src_map_type->key_type(), 
target_map_type->key_type()));
+                NeedsArrayConversionImpl(src_map_type->key_field(), 
target_map_type->key_field()));
             if (need_cast) {
                 return true;
             }
-            PAIMON_ASSIGN_OR_RAISE(
-                need_cast,
-                NeedCastArrayForTimestamp(src_map_type->item_type(), 
target_map_type->item_type()));
+            PAIMON_ASSIGN_OR_RAISE(need_cast,
+                                   
NeedsArrayConversionImpl(src_map_type->item_field(),
+                                                            
target_map_type->item_field()));
             return need_cast;
         }
         case arrow::Type::type::LIST: {
             auto* src_list_type = 
checked_cast<arrow::ListType*>(src_data_type.get());
             auto* target_list_type = 
checked_cast<arrow::ListType*>(target_data_type.get());
-            PAIMON_ASSIGN_OR_RAISE(bool need_cast,
-                                   
NeedCastArrayForTimestamp(src_list_type->value_type(),
-                                                             
target_list_type->value_type()));
-            return need_cast;
+            return NeedsArrayConversionImpl(src_list_type->value_field(),
+                                            target_list_type->value_field());
         }
         case arrow::Type::type::TIMESTAMP: {
             auto* src_ts_type = 
checked_cast<arrow::TimestampType*>(src_data_type.get());
             auto* target_ts_type = 
checked_cast<arrow::TimestampType*>(target_data_type.get());
-            if (src_ts_type->unit() != target_ts_type->unit() ||
-                src_ts_type->timezone() != target_ts_type->timezone()) {
-                return true;
-            }
-            return false;
+            return src_ts_type->unit() != target_ts_type->unit() ||
+                   src_ts_type->timezone() != target_ts_type->timezone();
         }
         default:
             return false;
     }
 }
 
-Result<std::shared_ptr<arrow::Array>> 
ParquetTimestampConverter::CastArrayForTimestamp(
+Result<std::shared_ptr<arrow::Array>> ParquetReadTypeAdapter::AdaptArray(
+    const std::shared_ptr<arrow::Array>& array,
+    const std::shared_ptr<arrow::DataType>& target_data_type,
+    const std::shared_ptr<arrow::MemoryPool>& arrow_pool) {
+    PAIMON_ASSIGN_OR_RAISE(bool need_cast, NeedsArrayConversion(array->type(), 
target_data_type));
+    if (!need_cast) {
+        return array;
+    }
+
+    PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Array> result,
+                           AdaptArrayImpl(array, target_data_type, 
arrow_pool));
+    PAIMON_ASSIGN_OR_RAISE(need_cast, NeedsArrayConversion(result->type(), 
target_data_type));
+    if (need_cast) {
+        return Status::Invalid(fmt::format(
+            "unexpected: in parquet, after AdaptArray, output type {} does not 
match target "
+            "type {}",
+            result->type()->ToString(), target_data_type->ToString()));
+    }
+    return result;
+}
+
+Result<std::shared_ptr<arrow::Array>> ParquetReadTypeAdapter::AdaptArrayImpl(
     const std::shared_ptr<arrow::Array>& array,
     const std::shared_ptr<arrow::DataType>& target_data_type,
     const std::shared_ptr<arrow::MemoryPool>& arrow_pool) {
@@ -171,7 +192,7 @@ Result<std::shared_ptr<arrow::Array>> 
ParquetTimestampConverter::CastArrayForTim
                 const auto& field = struct_array->field(i);
                 PAIMON_ASSIGN_OR_RAISE(
                     std::shared_ptr<arrow::Array> sub_array,
-                    CastArrayForTimestamp(field, 
target_data_type->field(i)->type(), arrow_pool));
+                    AdaptArrayImpl(field, target_data_type->field(i)->type(), 
arrow_pool));
                 target_sub_arrays.push_back(sub_array);
                 target_names.push_back(target_data_type->field(i)->name());
             }
@@ -187,10 +208,10 @@ Result<std::shared_ptr<arrow::Array>> 
ParquetTimestampConverter::CastArrayForTim
             auto* map_type = 
checked_cast<arrow::MapType*>(target_data_type.get());
             PAIMON_ASSIGN_OR_RAISE(
                 std::shared_ptr<arrow::Array> key_array,
-                CastArrayForTimestamp(map_array->keys(), map_type->key_type(), 
arrow_pool));
+                AdaptArrayImpl(map_array->keys(), map_type->key_type(), 
arrow_pool));
             PAIMON_ASSIGN_OR_RAISE(
                 std::shared_ptr<arrow::Array> item_array,
-                CastArrayForTimestamp(map_array->items(), 
map_type->item_type(), arrow_pool));
+                AdaptArrayImpl(map_array->items(), map_type->item_type(), 
arrow_pool));
             return std::make_shared<arrow::MapArray>(
                 arrow::map(key_array->type(), item_array->type()), 
map_array->length(),
                 map_array->value_offsets(), key_array, item_array, 
map_array->null_bitmap(),
@@ -201,7 +222,7 @@ Result<std::shared_ptr<arrow::Array>> 
ParquetTimestampConverter::CastArrayForTim
             auto* list_type = 
checked_cast<arrow::ListType*>(target_data_type.get());
             PAIMON_ASSIGN_OR_RAISE(
                 std::shared_ptr<arrow::Array> value_array,
-                CastArrayForTimestamp(list_array->values(), 
list_type->value_type(), arrow_pool));
+                AdaptArrayImpl(list_array->values(), list_type->value_type(), 
arrow_pool));
             return std::make_shared<arrow::ListArray>(
                 arrow::list(value_array->type()), list_array->length(), 
list_array->value_offsets(),
                 value_array, list_array->null_bitmap(), 
list_array->null_count(),
@@ -213,8 +234,8 @@ Result<std::shared_ptr<arrow::Array>> 
ParquetTimestampConverter::CastArrayForTim
             auto* ts_target_type = 
checked_cast<arrow::TimestampType*>(target_data_type.get());
             if (src_type->unit() == arrow::TimeUnit::type::MILLI &&
                 ts_target_type->unit() == arrow::TimeUnit::type::SECOND) {
-                // parquet writer do not support second, and it cast second to 
milli.
-                // Therefore, in paimon file reader, we cast from milli to 
second.
+                // Parquet does not support second-precision timestamps, so 
the writer stores
+                // them as milliseconds. Convert them back to seconds for the 
Paimon read type.
                 auto cast_executor = 
std::make_shared<TimestampToTimestampCastExecutor>();
                 PAIMON_ASSIGN_OR_RAISE(
                     std::shared_ptr<arrow::Array> target_array,
@@ -222,10 +243,9 @@ Result<std::shared_ptr<arrow::Array>> 
ParquetTimestampConverter::CastArrayForTim
                 return target_array;
             }
             if (src_type->timezone() != ts_target_type->timezone()) {
-                // 1. For nano type, parquet writer will write nano into int96 
type, which does
-                // not contain any stats or zone info. Therefore in paimon 
file reader, we add
-                // zone info according to target type 2. For other precision, 
parquet reader
-                // will return UTC tz. Therefore, in paimon file reader, we 
add local zone info.
+                // INT96 carries no timezone, while other Parquet timestamps 
may be exposed as
+                // UTC. Attach the timezone required by the Paimon read type 
without changing
+                // the underlying values.
                 
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Array> target_array,
                                                   
ts_array->View(target_data_type));
                 return target_array;
@@ -235,4 +255,5 @@ Result<std::shared_ptr<arrow::Array>> 
ParquetTimestampConverter::CastArrayForTim
             return array;
     }
 }
+
 }  // namespace paimon::parquet
diff --git a/src/paimon/format/parquet/parquet_timestamp_converter.h 
b/src/paimon/format/parquet/parquet_read_type_adapter.h
similarity index 59%
rename from src/paimon/format/parquet/parquet_timestamp_converter.h
rename to src/paimon/format/parquet/parquet_read_type_adapter.h
index 2954bfe4..2f296045 100644
--- a/src/paimon/format/parquet/parquet_timestamp_converter.h
+++ b/src/paimon/format/parquet/parquet_read_type_adapter.h
@@ -25,22 +25,34 @@
 
 namespace paimon::parquet {
 
-class ParquetTimestampConverter {
+class ParquetReadTypeAdapter {
  public:
-    ParquetTimestampConverter() = delete;
-    ~ParquetTimestampConverter() = delete;
+    ParquetReadTypeAdapter() = delete;
+    ~ParquetReadTypeAdapter() = delete;
 
-    static Result<std::shared_ptr<arrow::DataType>> AdjustTimezone(
+    static Result<std::shared_ptr<arrow::DataType>> NormalizeFileType(
         const std::shared_ptr<arrow::DataType>& src_data_type);
 
-    static Result<bool> NeedCastArrayForTimestamp(
+    static Result<bool> NeedsArrayConversion(
         const std::shared_ptr<arrow::DataType>& src_data_type,
         const std::shared_ptr<arrow::DataType>& target_data_type);
 
-    static Result<std::shared_ptr<arrow::Array>> CastArrayForTimestamp(
+    static Result<std::shared_ptr<arrow::Array>> AdaptArray(
         const std::shared_ptr<arrow::Array>& array,
         const std::shared_ptr<arrow::DataType>& target_data_type,
         const std::shared_ptr<arrow::MemoryPool>& arrow_pool);
+
+ private:
+    static Result<std::shared_ptr<arrow::Array>> AdaptArrayImpl(
+        const std::shared_ptr<arrow::Array>& array,
+        const std::shared_ptr<arrow::DataType>& target_data_type,
+        const std::shared_ptr<arrow::MemoryPool>& arrow_pool);
+
+    static Result<bool> NeedsArrayConversionImpl(const 
std::shared_ptr<arrow::Field>& src_field,
+                                                 const 
std::shared_ptr<arrow::Field>& target_field);
+
+    static bool IsCompatibleBlob(const std::shared_ptr<arrow::Field>& 
src_field,
+                                 const std::shared_ptr<arrow::Field>& 
target_field);
 };
 
 }  // namespace paimon::parquet
diff --git a/src/paimon/format/parquet/parquet_timestamp_converter_test.cpp 
b/src/paimon/format/parquet/parquet_read_type_adapter_test.cpp
similarity index 76%
rename from src/paimon/format/parquet/parquet_timestamp_converter_test.cpp
rename to src/paimon/format/parquet/parquet_read_type_adapter_test.cpp
index 784eecd0..17933cb8 100644
--- a/src/paimon/format/parquet/parquet_timestamp_converter_test.cpp
+++ b/src/paimon/format/parquet/parquet_read_type_adapter_test.cpp
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-#include "paimon/format/parquet/parquet_timestamp_converter.h"
+#include "paimon/format/parquet/parquet_read_type_adapter.h"
 
 #include <memory>
 
@@ -31,7 +31,7 @@
 
 namespace paimon::parquet::test {
 
-TEST(ParquetTimestampConverterTest, TestNeedCastArrayForTimestamp) {
+TEST(ParquetReadTypeAdapterTest, TestNeedsArrayConversion) {
     {
         // single field need cast
         arrow::FieldVector fields = {
@@ -40,10 +40,10 @@ TEST(ParquetTimestampConverterTest, 
TestNeedCastArrayForTimestamp) {
         arrow::FieldVector target_fields = {
             arrow::field("f0", arrow::timestamp(arrow::TimeUnit::NANO, "UTC")),
         };
-        ASSERT_OK_AND_ASSIGN(bool need_cast,
-                             
ParquetTimestampConverter::NeedCastArrayForTimestamp(
+        ASSERT_OK_AND_ASSIGN(bool needs_conversion,
+                             ParquetReadTypeAdapter::NeedsArrayConversion(
                                  arrow::struct_(fields), 
arrow::struct_(target_fields)));
-        ASSERT_TRUE(need_cast);
+        ASSERT_TRUE(needs_conversion);
     }
     {
         // field in list need cast
@@ -52,10 +52,10 @@ TEST(ParquetTimestampConverterTest, 
TestNeedCastArrayForTimestamp) {
         };
         arrow::FieldVector target_fields = {
             arrow::field("f2", 
arrow::list(arrow::timestamp(arrow::TimeUnit::SECOND)))};
-        ASSERT_OK_AND_ASSIGN(bool need_cast,
-                             
ParquetTimestampConverter::NeedCastArrayForTimestamp(
+        ASSERT_OK_AND_ASSIGN(bool needs_conversion,
+                             ParquetReadTypeAdapter::NeedsArrayConversion(
                                  arrow::struct_(fields), 
arrow::struct_(target_fields)));
-        ASSERT_TRUE(need_cast);
+        ASSERT_TRUE(needs_conversion);
     }
     {
         // field in map need cast
@@ -66,10 +66,10 @@ TEST(ParquetTimestampConverterTest, 
TestNeedCastArrayForTimestamp) {
         arrow::FieldVector target_fields = {
             arrow::field("f1", 
arrow::map(arrow::timestamp(arrow::TimeUnit::SECOND),
                                           
arrow::timestamp(arrow::TimeUnit::NANO, "UTC")))};
-        ASSERT_OK_AND_ASSIGN(bool need_cast,
-                             
ParquetTimestampConverter::NeedCastArrayForTimestamp(
+        ASSERT_OK_AND_ASSIGN(bool needs_conversion,
+                             ParquetReadTypeAdapter::NeedsArrayConversion(
                                  arrow::struct_(fields), 
arrow::struct_(target_fields)));
-        ASSERT_TRUE(need_cast);
+        ASSERT_TRUE(needs_conversion);
     }
     {
         // field in struct need cast
@@ -84,32 +84,32 @@ TEST(ParquetTimestampConverterTest, 
TestNeedCastArrayForTimestamp) {
                              {arrow::field("f0", 
arrow::timestamp(arrow::TimeUnit::MILLI)),
                               arrow::field("f1", 
arrow::timestamp(arrow::TimeUnit::NANO, "UTC"))})),
         };
-        ASSERT_OK_AND_ASSIGN(bool need_cast,
-                             
ParquetTimestampConverter::NeedCastArrayForTimestamp(
+        ASSERT_OK_AND_ASSIGN(bool needs_conversion,
+                             ParquetReadTypeAdapter::NeedsArrayConversion(
                                  arrow::struct_(fields), 
arrow::struct_(target_fields)));
-        ASSERT_TRUE(need_cast);
+        ASSERT_TRUE(needs_conversion);
     }
 }
 
-TEST(ParquetTimestampConverterTest, TestAlreadyLogicalBlobCompatibility) {
+TEST(ParquetReadTypeAdapterTest, TestAlreadyLogicalBlobCompatibility) {
     std::shared_ptr<arrow::Field> restored_blob = arrow::field("blob", 
arrow::large_binary());
     std::shared_ptr<arrow::Field> expected_physical_blob =
         BlobUtils::ToArrowField("blob")->WithType(arrow::binary());
 
-    ASSERT_OK_AND_ASSIGN(bool need_cast, 
ParquetTimestampConverter::NeedCastArrayForTimestamp(
-                                             arrow::struct_({restored_blob}),
-                                             
arrow::struct_({expected_physical_blob})));
-    ASSERT_FALSE(need_cast);
+    ASSERT_OK_AND_ASSIGN(bool needs_conversion, 
ParquetReadTypeAdapter::NeedsArrayConversion(
+                                                    
arrow::struct_({restored_blob}),
+                                                    
arrow::struct_({expected_physical_blob})));
+    ASSERT_FALSE(needs_conversion);
 
     // Only a binary field carrying Paimon BLOB metadata is compatible with a 
large_binary field
     // already restored from ARROW:schema. A plain binary field must still be 
rejected.
-    ASSERT_NOK_WITH_MSG(ParquetTimestampConverter::NeedCastArrayForTimestamp(
+    ASSERT_NOK_WITH_MSG(ParquetReadTypeAdapter::NeedsArrayConversion(
                             arrow::struct_({restored_blob}),
                             arrow::struct_({arrow::field("blob", 
arrow::binary())})),
-                        "src type large_binary and target type binary 
mismatch");
+                        "source type large_binary and target type binary 
mismatch");
 }
 
-TEST(ParquetTimestampConverterTest, TestCastArrayForTimestamp) {
+TEST(ParquetReadTypeAdapterTest, TestAdaptArray) {
     auto timezone = DateTimeUtils::GetLocalTimezoneName();
     arrow::FieldVector fields = {
         arrow::field("f1", arrow::map(arrow::timestamp(arrow::TimeUnit::MILLI),
@@ -141,9 +141,9 @@ TEST(ParquetTimestampConverterTest, 
TestCastArrayForTimestamp) {
             .ValueOrDie());
 
     std::shared_ptr<arrow::MemoryPool> pool = GetArrowPool(GetDefaultPool());
-    ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::Array> result_array,
-                         ParquetTimestampConverter::CastArrayForTimestamp(
-                             array, arrow::struct_(target_fields), pool));
+    ASSERT_OK_AND_ASSIGN(
+        std::shared_ptr<arrow::Array> result_array,
+        ParquetReadTypeAdapter::AdaptArray(array, 
arrow::struct_(target_fields), pool));
 
     auto expected_array = std::dynamic_pointer_cast<arrow::StructArray>(
         
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_(target_fields), R"([
@@ -155,7 +155,20 @@ TEST(ParquetTimestampConverterTest, 
TestCastArrayForTimestamp) {
     ASSERT_TRUE(result_array->Equals(expected_array)) << 
result_array->ToString();
 }
 
-TEST(ParquetTimestampConverterTest, TestAdjustTimezone) {
+TEST(ParquetReadTypeAdapterTest, TestAdaptArrayValidatesResultType) {
+    std::shared_ptr<arrow::DataType> src_type = 
arrow::timestamp(arrow::TimeUnit::MICRO);
+    std::shared_ptr<arrow::DataType> target_type = 
arrow::timestamp(arrow::TimeUnit::MILLI);
+    std::shared_ptr<arrow::Array> array =
+        arrow::ipc::internal::json::ArrayFromJSON(src_type, R"(["1970-01-01 
00:00:01"])")
+            .ValueOrDie();
+    std::shared_ptr<arrow::MemoryPool> pool = GetArrowPool(GetDefaultPool());
+
+    ASSERT_NOK_WITH_MSG(ParquetReadTypeAdapter::AdaptArray(array, target_type, 
pool),
+                        "after AdaptArray, output type timestamp[us] does not 
match target type "
+                        "timestamp[ms]");
+}
+
+TEST(ParquetReadTypeAdapterTest, TestNormalizeFileType) {
     auto timezone = DateTimeUtils::GetLocalTimezoneName();
     arrow::FieldVector fields = {
         arrow::field("f1", arrow::map(arrow::timestamp(arrow::TimeUnit::MILLI),
@@ -179,8 +192,8 @@ TEST(ParquetTimestampConverterTest, TestAdjustTimezone) {
         arrow::field("f4", arrow::timestamp(arrow::TimeUnit::NANO)),
     };
 
-    ASSERT_OK_AND_ASSIGN(auto result_type,
-                         
ParquetTimestampConverter::AdjustTimezone(arrow::struct_(fields)));
+    ASSERT_OK_AND_ASSIGN(std::shared_ptr<arrow::DataType> result_type,
+                         
ParquetReadTypeAdapter::NormalizeFileType(arrow::struct_(fields)));
     ASSERT_TRUE(result_type->Equals(arrow::struct_(target_fields)));
 }
 }  // namespace paimon::parquet::test
diff --git a/test/inte/paimon_read_compat_inte_test.cpp 
b/test/inte/paimon_read_compat_inte_test.cpp
index 94d6c99f..a5595150 100644
--- a/test/inte/paimon_read_compat_inte_test.cpp
+++ b/test/inte/paimon_read_compat_inte_test.cpp
@@ -419,6 +419,29 @@ TEST_P(PaimonReadCompatInteTest, ReadsBlobValues) {
     ASSERT_EQ(inline_descriptor->Uri(), 
"file:///nonexistent/pypaimon-all-types-external-blob.bin");
     ASSERT_EQ(inline_descriptor->Offset(), 7);
     ASSERT_EQ(inline_descriptor->Length(), 11);
+
+    // Python and Java store MAP<STRING, BLOB> values in standard separate 
BLOB files. Validate
+    // resolved payloads, null values, and an empty map. Rust stores raw 
values inline in Parquet,
+    // which is not a compatible Paimon BLOB representation and is asserted 
separately below.
+    if (writer_prefix == "rust") {
+        return;
+    }
+    ASSERT_OK_AND_ASSIGN(
+        std::shared_ptr<arrow::ChunkedArray> map_blob_result,
+        ReadTable(writer_prefix + "_map_blob_types", {"id", "f_map_blob"}, 
blob_value_options));
+    std::shared_ptr<arrow::StructArray> map_blob_rows = 
GetOnlyStructChunk(map_blob_result);
+    ASSERT_TRUE(map_blob_rows);
+    ASSERT_EQ(map_blob_rows->length(), 3);
+    AssertFieldEqualsJson(map_blob_rows, "id", arrow::int32(), "[1, 2, 3]");
+    AssertFieldEqualsJson(map_blob_rows, "f_map_blob",
+                          arrow::map(arrow::utf8(), arrow::large_binary()),
+                          R"([[ ["left", "blob-map-left"], ["right", null] ], 
null, []])");
+}
+
+TEST(PaimonReadCompatInteStandaloneTest, RejectsNonStandardRustMapBlob) {
+    ASSERT_NOK_WITH_MSG(
+        ReadTable("rust_map_blob_types", {"f_map_blob"}),
+        "Parquet does not support partial projection inside list/map: src 
map<string, binary");
 }
 
 TEST_P(PaimonReadCompatInteTest, ReadsVectorValues) {
@@ -484,7 +507,8 @@ TEST_P(PaimonUnsupportedTypeInteTest, ReportsExpectedError) 
{
 
 std::vector<UnsupportedReadParam> UnsupportedReadParams() {
     const std::vector<UnsupportedReadCase> read_cases = {
-        {"NestedBlob", "nested_blob_types", "f_array_blob", "Blob field must 
be a top-level field"},
+        {"ArrayBlob", "array_blob_types", "f_array_blob",
+         "BLOB field must be a top-level field or the direct value of a 
top-level MAP field"},
         {"TimePrecision0", "time_types", "f_time_0", "Unsupported type: TIME"},
         {"TimePrecision3", "time_types", "f_time_3", "Unsupported type: TIME"},
         {"TimePrecision6", "time_types", "f_time_6", "Unsupported type: TIME"},
diff --git a/test/test_data/parquet/append_types_compatibility.db/README.md 
b/test/test_data/parquet/append_types_compatibility.db/README.md
index 5e4e330b..a1ac373a 100644
--- a/test/test_data/parquet/append_types_compatibility.db/README.md
+++ b/test/test_data/parquet/append_types_compatibility.db/README.md
@@ -19,7 +19,9 @@
 
 # Paimon writer compatibility fixtures
 
-The `append_types_compatibility.db` database contains equivalent append-only 
tables written by Python, Rust, and Java Paimon. Table names use the writer as 
a prefix and describe the group of types in that table; no individual table is 
claimed to contain every Paimon type.
+The `append_types_compatibility.db` database contains append-only 
compatibility tables written by
+Python, Rust, and Java Paimon. Table names use the writer as a prefix and 
describe the group of
+types in that table; no individual table is claimed to contain every Paimon 
type.
 
 The writers and Parquet implementations are:
 
@@ -29,11 +31,16 @@ The writers and Parquet implementations are:
 
 ## Table groups
 
-Each writer prefix has four tables with the same schema and logical values:
+Each writer prefix has five tables with the same declared schema and intended 
logical values:
 
 - `<writer>_types`: 43 columns and 3 rows covering integral and floating 
types, BOOLEAN, CHAR/VARCHAR/STRING, BINARY/VARBINARY/BYTES, BLOB, DATE, 
TIMESTAMP and TIMESTAMP_LTZ at precision 0/3/6/9, multiple DECIMAL precisions, 
VARIANT, ARRAY, MAP, ROW, and deeply nested combinations. Row 2 is null in 
every nullable column and row 3 exercises empty values.
 - `<writer>_vector_types`: VECTOR length 3 for BOOLEAN, TINYINT, SMALLINT, 
INT, BIGINT, FLOAT, and DOUBLE.
-- `<writer>_nested_blob_types`: ARRAY&lt;BLOB&gt; and MAP&lt;STRING, BLOB&gt;. 
Paimon C++ rejects the table schema because ARRAY&lt;BLOB&gt; is unsupported. 
Schema validation happens before column projection, so the compatibility test 
explicitly checks this error instead of reading either field.
+- `<writer>_array_blob_types`: Paimon C++ does not currently support 
ARRAY&lt;BLOB&gt;.
+- `<writer>_map_blob_types`: MAP&lt;STRING, BLOB&gt;, including non-null, 
null, and empty map
+  values. Python and Java store these values in standard separate BLOB files, 
which Paimon C++
+  can read. Rust stores the raw values inline as Parquet `binary`; this is not 
the Paimon BLOB
+  descriptor representation, cannot be read as BLOB by Java, and is 
intentionally rejected by
+  Paimon C++.
 - `<writer>_time_types`: TIME declarations at precision 0/3/6/9. Paimon C++ 
currently rejects `TIME` while parsing the table schema.
 
 The tables are separated because Paimon C++ does not allow VECTOR in a 
data-evolution table, BLOB requires data evolution, and a schema-level 
incompatibility must not prevent compatible columns from being tested.
@@ -42,4 +49,7 @@ The tables are separated because Paimon C++ does not allow 
VECTOR in a data-evol
 
 The `f_blob_descriptor` value intentionally points at a nonexistent external 
URI. Read it with `blob-as-descriptor=true`; it exercises the inline descriptor 
and Parquet `ARROW:schema` path rather than external blob fetching.
 
-Both PyArrow and parquet-rs persist the original Arrow type in `ARROW:schema`, 
but the stored types differ in these fixtures. The Python fixture records the 
physical Parquet `binary` column as Arrow `large_binary`, while the Rust 
fixture records it as Arrow `binary`. The compatibility test verifies that 
Paimon C++ can read both representations and preserve the descriptor value.
+Both PyArrow and parquet-rs persist the original Arrow type in `ARROW:schema`, 
but the stored types
+differ for `f_blob_descriptor`. The Python fixture records the physical 
Parquet `binary` column as
+Arrow `large_binary`, while the Rust fixture records it as Arrow `binary`. The 
compatibility test
+verifies that Paimon C++ can read both representations and preserve the 
descriptor value.
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/bucket-0/data-08f384c4-ca25-4b99-a024-07dc9526b5dc-0.parquet
 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/bucket-0/data-8a49858f-c0a7-468e-8d71-45b65142d422-0.parquet
similarity index 100%
copy from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/bucket-0/data-08f384c4-ca25-4b99-a024-07dc9526b5dc-0.parquet
copy to 
test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/bucket-0/data-8a49858f-c0a7-468e-8d71-45b65142d422-0.parquet
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/bucket-0/data-08f384c4-ca25-4b99-a024-07dc9526b5dc-1.blob
 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/bucket-0/data-8a49858f-c0a7-468e-8d71-45b65142d422-1.blob
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/bucket-0/data-08f384c4-ca25-4b99-a024-07dc9526b5dc-1.blob
rename to 
test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/bucket-0/data-8a49858f-c0a7-468e-8d71-45b65142d422-1.blob
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-7de5f36f-9ba7-4853-b344-cedc5ba17995-0
 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/manifest/manifest-4db45a0c-f933-4043-80de-f1d5a5107966-0
similarity index 88%
copy from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-7de5f36f-9ba7-4853-b344-cedc5ba17995-0
copy to 
test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/manifest/manifest-4db45a0c-f933-4043-80de-f1d5a5107966-0
index f12e5f84..f6ca091d 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-7de5f36f-9ba7-4853-b344-cedc5ba17995-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/manifest/manifest-4db45a0c-f933-4043-80de-f1d5a5107966-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0
 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/manifest/manifest-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-0
similarity index 95%
copy from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0
copy to 
test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/manifest/manifest-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-0
index 034d68e1..4b7ac1f0 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/manifest/manifest-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0
 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/manifest/manifest-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-1
similarity index 86%
copy from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0
copy to 
test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/manifest/manifest-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-1
index 034d68e1..b7ae3ef2 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/manifest/manifest-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-1
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/schema/schema-0
 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/schema/schema-0
similarity index 65%
copy from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/schema/schema-0
copy to 
test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/schema/schema-0
index 78945df1..0bce94c9 100644
--- 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/schema/schema-0
+++ 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/schema/schema-0
@@ -12,25 +12,17 @@
       "type" : "ARRAY",
       "element" : "BLOB"
     }
-  }, {
-    "id" : 2,
-    "name" : "f_map_blob",
-    "type" : {
-      "type" : "MAP",
-      "key" : "STRING NOT NULL",
-      "value" : "BLOB"
-    }
   } ],
-  "highestFieldId" : 2,
+  "highestFieldId" : 1,
   "partitionKeys" : [ ],
   "primaryKeys" : [ ],
   "options" : {
     "bucket" : "-1",
-    "blob-field" : "f_array_blob,f_map_blob",
+    "blob-field" : "f_array_blob",
     "row-tracking.enabled" : "true",
     "data-evolution.enabled" : "true",
     "manifest.format" : "avro",
     "file.format" : "parquet"
   },
-  "timeMillis" : 1788853436235
+  "timeMillis" : 1788859707631
 }
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/EARLIEST
 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/snapshot/EARLIEST
similarity index 100%
copy from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/EARLIEST
copy to 
test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/snapshot/EARLIEST
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/LATEST
 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/snapshot/LATEST
similarity index 100%
copy from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/LATEST
copy to 
test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/snapshot/LATEST
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/snapshot/snapshot-1
 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/snapshot/snapshot-1
new file mode 100644
index 00000000..29a83ff0
--- /dev/null
+++ 
b/test/test_data/parquet/append_types_compatibility.db/java_array_blob_types/snapshot/snapshot-1
@@ -0,0 +1,18 @@
+{
+  "version" : 3,
+  "uuid" : "829d0558-0c81-4701-ba81-108a84c1de2e",
+  "id" : 1,
+  "schemaId" : 0,
+  "baseManifestList" : "manifest-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-0",
+  "baseManifestListSize" : 1006,
+  "deltaManifestList" : "manifest-list-38d776fc-d73a-4d8b-a166-9fc47b88c3bb-1",
+  "deltaManifestListSize" : 1115,
+  "commitUser" : "389ad12e-4421-4e4f-ad11-14284923cc57",
+  "writerVersion" : "java-2-ali-2.6-94eb84aee341e9ad221c027ed97c8766583a7abe",
+  "commitIdentifier" : 9223372036854775807,
+  "commitKind" : "APPEND",
+  "timeMillis" : 1788859708818,
+  "totalRecordCount" : 6,
+  "deltaRecordCount" : 6,
+  "nextRowId" : 3
+}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/bucket-0/data-08f384c4-ca25-4b99-a024-07dc9526b5dc-0.parquet
 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/bucket-0/data-d2b0d467-f565-4453-b400-468f9ec22a54-0.parquet
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/bucket-0/data-08f384c4-ca25-4b99-a024-07dc9526b5dc-0.parquet
rename to 
test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/bucket-0/data-d2b0d467-f565-4453-b400-468f9ec22a54-0.parquet
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/bucket-0/data-08f384c4-ca25-4b99-a024-07dc9526b5dc-2.blob
 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/bucket-0/data-d2b0d467-f565-4453-b400-468f9ec22a54-1.blob
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/bucket-0/data-08f384c4-ca25-4b99-a024-07dc9526b5dc-2.blob
rename to 
test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/bucket-0/data-d2b0d467-f565-4453-b400-468f9ec22a54-1.blob
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-7de5f36f-9ba7-4853-b344-cedc5ba17995-0
 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/manifest/manifest-62c45eba-0a54-4a63-b89d-0ab09a768412-0
similarity index 88%
rename from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-7de5f36f-9ba7-4853-b344-cedc5ba17995-0
rename to 
test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/manifest/manifest-62c45eba-0a54-4a63-b89d-0ab09a768412-0
index f12e5f84..c803b1c9 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-7de5f36f-9ba7-4853-b344-cedc5ba17995-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/manifest/manifest-62c45eba-0a54-4a63-b89d-0ab09a768412-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-1
 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/manifest/manifest-list-d3262408-2da8-408b-9c67-b487ecf8565b-0
similarity index 86%
rename from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-1
rename to 
test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/manifest/manifest-list-d3262408-2da8-408b-9c67-b487ecf8565b-0
index 07144721..655f831b 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-1
 and 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/manifest/manifest-list-d3262408-2da8-408b-9c67-b487ecf8565b-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0
 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/manifest/manifest-list-d3262408-2da8-408b-9c67-b487ecf8565b-1
similarity index 86%
rename from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0
rename to 
test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/manifest/manifest-list-d3262408-2da8-408b-9c67-b487ecf8565b-1
index 034d68e1..9040aaea 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/manifest/manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/manifest/manifest-list-d3262408-2da8-408b-9c67-b487ecf8565b-1
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/schema/schema-0
 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/schema/schema-0
similarity index 68%
rename from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/schema/schema-0
rename to 
test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/schema/schema-0
index 78945df1..340c0ade 100644
--- 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/schema/schema-0
+++ 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/schema/schema-0
@@ -7,13 +7,6 @@
     "type" : "INT NOT NULL"
   }, {
     "id" : 1,
-    "name" : "f_array_blob",
-    "type" : {
-      "type" : "ARRAY",
-      "element" : "BLOB"
-    }
-  }, {
-    "id" : 2,
     "name" : "f_map_blob",
     "type" : {
       "type" : "MAP",
@@ -21,16 +14,16 @@
       "value" : "BLOB"
     }
   } ],
-  "highestFieldId" : 2,
+  "highestFieldId" : 1,
   "partitionKeys" : [ ],
   "primaryKeys" : [ ],
   "options" : {
     "bucket" : "-1",
-    "blob-field" : "f_array_blob,f_map_blob",
+    "blob-field" : "f_map_blob",
     "row-tracking.enabled" : "true",
     "data-evolution.enabled" : "true",
     "manifest.format" : "avro",
     "file.format" : "parquet"
   },
-  "timeMillis" : 1788853436235
+  "timeMillis" : 1788859708882
 }
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/EARLIEST
 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/snapshot/EARLIEST
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/EARLIEST
rename to 
test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/snapshot/EARLIEST
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/snapshot/LATEST
 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/snapshot/LATEST
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/snapshot/LATEST
rename to 
test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/snapshot/LATEST
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/snapshot/snapshot-1
 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/snapshot/snapshot-1
new file mode 100644
index 00000000..edcebd32
--- /dev/null
+++ 
b/test/test_data/parquet/append_types_compatibility.db/java_map_blob_types/snapshot/snapshot-1
@@ -0,0 +1,18 @@
+{
+  "version" : 3,
+  "uuid" : "10cfbd5d-65f3-4ef3-b8b0-78c34e831369",
+  "id" : 1,
+  "schemaId" : 0,
+  "baseManifestList" : "manifest-list-d3262408-2da8-408b-9c67-b487ecf8565b-0",
+  "baseManifestListSize" : 1006,
+  "deltaManifestList" : "manifest-list-d3262408-2da8-408b-9c67-b487ecf8565b-1",
+  "deltaManifestListSize" : 1115,
+  "commitUser" : "df37a7f9-0e78-4d8c-aa5c-13c284be6a37",
+  "writerVersion" : "java-2-ali-2.6-94eb84aee341e9ad221c027ed97c8766583a7abe",
+  "commitIdentifier" : 9223372036854775807,
+  "commitKind" : "APPEND",
+  "timeMillis" : 1788859708907,
+  "totalRecordCount" : 6,
+  "deltaRecordCount" : 6,
+  "nextRowId" : 3
+}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/snapshot-1
 
b/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/snapshot-1
deleted file mode 100644
index b98c7d14..00000000
--- 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/snapshot-1
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "version" : 3,
-  "uuid" : "89919eb0-8f08-4098-9002-d19680ca3a6d",
-  "id" : 1,
-  "schemaId" : 0,
-  "baseManifestList" : "manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-0",
-  "baseManifestListSize" : 1006,
-  "deltaManifestList" : "manifest-list-2b993fd4-7b01-4cc4-925f-aa69eccf1a2e-1",
-  "deltaManifestListSize" : 1111,
-  "commitUser" : "1192f15e-31f1-42a4-b498-248a36a42985",
-  "writerVersion" : "java-2-ali-2.6-94eb84aee341e9ad221c027ed97c8766583a7abe",
-  "commitIdentifier" : 9223372036854775807,
-  "commitKind" : "APPEND",
-  "timeMillis" : 1788853436256,
-  "totalRecordCount" : 9,
-  "deltaRecordCount" : 9,
-  "nextRowId" : 3
-}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/bucket-0/data-7b1104e4-657e-40ea-8ac5-ecac0ecbc3bf-0.blob
 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/bucket-0/data-043fd246-38df-40f7-9147-c1c416282ecc-0.blob
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/bucket-0/data-7b1104e4-657e-40ea-8ac5-ecac0ecbc3bf-0.blob
rename to 
test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/bucket-0/data-043fd246-38df-40f7-9147-c1c416282ecc-0.blob
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/bucket-0/data-2f2a8c74-bd24-4106-9ed5-91d20d711270-0.parquet
 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/bucket-0/data-3cb0cb1a-5db9-478e-a727-6f34d6e800e5-0.parquet
similarity index 100%
copy from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/bucket-0/data-2f2a8c74-bd24-4106-9ed5-91d20d711270-0.parquet
copy to 
test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/bucket-0/data-3cb0cb1a-5db9-478e-a727-6f34d6e800e5-0.parquet
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-0ef6e952-7e56-4135-9ab0-d6e79820dfff-0
 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/manifest/manifest-8e8abfa0-a7fe-4669-a7be-cd095dee5fac-0
similarity index 87%
copy from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-0ef6e952-7e56-4135-9ab0-d6e79820dfff-0
copy to 
test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/manifest/manifest-8e8abfa0-a7fe-4669-a7be-cd095dee5fac-0
index eb9d186f..3c001fe9 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-0ef6e952-7e56-4135-9ab0-d6e79820dfff-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/manifest/manifest-8e8abfa0-a7fe-4669-a7be-cd095dee5fac-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0
 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/manifest/manifest-list-2f17c472-fea6-42d0-8300-12da4874d53a-0
similarity index 96%
copy from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0
copy to 
test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/manifest/manifest-list-2f17c472-fea6-42d0-8300-12da4874d53a-0
index 8608500a..105bce89 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/manifest/manifest-list-2f17c472-fea6-42d0-8300-12da4874d53a-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-1
 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/manifest/manifest-list-2f17c472-fea6-42d0-8300-12da4874d53a-1
similarity index 85%
rename from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-1
rename to 
test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/manifest/manifest-list-2f17c472-fea6-42d0-8300-12da4874d53a-1
index c85847cb..cf8d9f9c 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-1
 and 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/manifest/manifest-list-2f17c472-fea6-42d0-8300-12da4874d53a-1
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/schema/schema-0
 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/schema/schema-0
similarity index 56%
copy from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/schema/schema-0
copy to 
test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/schema/schema-0
index e311af94..9eff531e 100644
--- 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/schema/schema-0
+++ 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/schema/schema-0
@@ -15,19 +15,9 @@
         "element": "BLOB",
         "nullable": true
       }
-    },
-    {
-      "id": 2,
-      "name": "f_map_blob",
-      "type": {
-        "type": "MAP<STRING NOT NULL, BLOB>",
-        "key": "STRING NOT NULL",
-        "value": "BLOB",
-        "nullable": true
-      }
     }
   ],
-  "highestFieldId": 2,
+  "highestFieldId": 1,
   "partitionKeys": [],
   "primaryKeys": [],
   "options": {
@@ -36,8 +26,8 @@
     "manifest.format": "avro",
     "row-tracking.enabled": "true",
     "data-evolution.enabled": "true",
-    "blob-field": "f_array_blob,f_map_blob"
+    "blob-field": "f_array_blob"
   },
-  "comment": "Generated by pypaimon 2.0.0 with ARRAY<BLOB> and MAP<STRING, 
BLOB>.",
-  "timeMillis": 1788513128455
+  "comment": "Generated by pypaimon 2.0.0 with ARRAY<BLOB>.",
+  "timeMillis": 1788859561835
 }
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/snapshot/LATEST
 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/snapshot/LATEST
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/snapshot/LATEST
rename to 
test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/snapshot/LATEST
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/snapshot/snapshot-1
 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/snapshot/snapshot-1
new file mode 100644
index 00000000..ac1f6f1a
--- /dev/null
+++ 
b/test/test_data/parquet/append_types_compatibility.db/python_array_blob_types/snapshot/snapshot-1
@@ -0,0 +1,15 @@
+{
+  "version": 3,
+  "id": 1,
+  "schemaId": 0,
+  "baseManifestList": "manifest-list-2f17c472-fea6-42d0-8300-12da4874d53a-0",
+  "deltaManifestList": "manifest-list-2f17c472-fea6-42d0-8300-12da4874d53a-1",
+  "totalRecordCount": 6,
+  "deltaRecordCount": 6,
+  "commitUser": "657b7800-ea12-4a26-8075-4b2fd4e20d51",
+  "commitIdentifier": 9223372036854775807,
+  "commitKind": "APPEND",
+  "timeMillis": 1788859561849,
+  "nextRowId": 3,
+  "uuid": "43460238-f728-4ee1-8600-1f3d0c13234a"
+}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/bucket-0/data-2f2a8c74-bd24-4106-9ed5-91d20d711270-0.parquet
 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/bucket-0/data-4575b349-773b-4e2f-b364-ad98ee38fa72-0.parquet
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/bucket-0/data-2f2a8c74-bd24-4106-9ed5-91d20d711270-0.parquet
rename to 
test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/bucket-0/data-4575b349-773b-4e2f-b364-ad98ee38fa72-0.parquet
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/bucket-0/data-3850273b-000b-4248-add7-b30a6c7bb5e1-0.blob
 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/bucket-0/data-6e33a029-ca69-4453-a05b-2f7ed1a93f63-0.blob
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/bucket-0/data-3850273b-000b-4248-add7-b30a6c7bb5e1-0.blob
rename to 
test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/bucket-0/data-6e33a029-ca69-4453-a05b-2f7ed1a93f63-0.blob
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-0ef6e952-7e56-4135-9ab0-d6e79820dfff-0
 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/manifest/manifest-59ba3981-44aa-4af6-960e-9048ce6e883b-0
similarity index 87%
rename from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-0ef6e952-7e56-4135-9ab0-d6e79820dfff-0
rename to 
test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/manifest/manifest-59ba3981-44aa-4af6-960e-9048ce6e883b-0
index eb9d186f..f238c7da 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-0ef6e952-7e56-4135-9ab0-d6e79820dfff-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/manifest/manifest-59ba3981-44aa-4af6-960e-9048ce6e883b-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0
 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/manifest/manifest-list-c762398d-e22c-4676-b43d-4564909a0859-0
similarity index 96%
copy from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0
copy to 
test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/manifest/manifest-list-c762398d-e22c-4676-b43d-4564909a0859-0
index 8608500a..97bbfb6e 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/manifest/manifest-list-c762398d-e22c-4676-b43d-4564909a0859-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0
 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/manifest/manifest-list-c762398d-e22c-4676-b43d-4564909a0859-1
similarity index 85%
rename from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0
rename to 
test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/manifest/manifest-list-c762398d-e22c-4676-b43d-4564909a0859-1
index 8608500a..ad23ae47 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/manifest/manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/manifest/manifest-list-c762398d-e22c-4676-b43d-4564909a0859-1
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/schema/schema-0
 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/schema/schema-0
similarity index 61%
rename from 
test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/schema/schema-0
rename to 
test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/schema/schema-0
index e311af94..08ed496f 100644
--- 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/schema/schema-0
+++ 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/schema/schema-0
@@ -9,15 +9,6 @@
     },
     {
       "id": 1,
-      "name": "f_array_blob",
-      "type": {
-        "type": "ARRAY",
-        "element": "BLOB",
-        "nullable": true
-      }
-    },
-    {
-      "id": 2,
       "name": "f_map_blob",
       "type": {
         "type": "MAP<STRING NOT NULL, BLOB>",
@@ -27,7 +18,7 @@
       }
     }
   ],
-  "highestFieldId": 2,
+  "highestFieldId": 1,
   "partitionKeys": [],
   "primaryKeys": [],
   "options": {
@@ -36,8 +27,8 @@
     "manifest.format": "avro",
     "row-tracking.enabled": "true",
     "data-evolution.enabled": "true",
-    "blob-field": "f_array_blob,f_map_blob"
+    "blob-field": "f_map_blob"
   },
-  "comment": "Generated by pypaimon 2.0.0 with ARRAY<BLOB> and MAP<STRING, 
BLOB>.",
-  "timeMillis": 1788513128455
+  "comment": "Generated by pypaimon 2.0.0 with MAP<STRING, BLOB>.",
+  "timeMillis": 1788859561850
 }
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/LATEST
 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/snapshot/LATEST
similarity index 100%
copy from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/LATEST
copy to 
test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/snapshot/LATEST
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/snapshot/snapshot-1
 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/snapshot/snapshot-1
new file mode 100644
index 00000000..abae40fe
--- /dev/null
+++ 
b/test/test_data/parquet/append_types_compatibility.db/python_map_blob_types/snapshot/snapshot-1
@@ -0,0 +1,15 @@
+{
+  "version": 3,
+  "id": 1,
+  "schemaId": 0,
+  "baseManifestList": "manifest-list-c762398d-e22c-4676-b43d-4564909a0859-0",
+  "deltaManifestList": "manifest-list-c762398d-e22c-4676-b43d-4564909a0859-1",
+  "totalRecordCount": 6,
+  "deltaRecordCount": 6,
+  "commitUser": "cadd033b-11dd-40e2-a759-0306ab9df091",
+  "commitIdentifier": 9223372036854775807,
+  "commitKind": "APPEND",
+  "timeMillis": 1788859561854,
+  "nextRowId": 3,
+  "uuid": "0279865a-f61e-41bf-8218-69adf804978f"
+}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/snapshot/snapshot-1
 
b/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/snapshot/snapshot-1
deleted file mode 100644
index 8e955da7..00000000
--- 
a/test/test_data/parquet/append_types_compatibility.db/python_nested_blob_types/snapshot/snapshot-1
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "version": 3,
-  "id": 1,
-  "schemaId": 0,
-  "baseManifestList": "manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-0",
-  "deltaManifestList": "manifest-list-19134fd4-d60f-460e-b880-2633ef1b5a8c-1",
-  "totalRecordCount": 9,
-  "deltaRecordCount": 9,
-  "commitUser": "297eeaeb-5c62-4b3c-8f9e-950b09612bc3",
-  "commitIdentifier": 9223372036854775807,
-  "commitKind": "APPEND",
-  "timeMillis": 1788513128459,
-  "nextRowId": 3,
-  "uuid": "d2837b32-27c3-453e-8470-ccb9d176d3f0"
-}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/bucket-0/data-334e3ff1-32c3-4f59-98c1-7e366b0f4b0b-0.parquet
 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/bucket-0/data-334e3ff1-32c3-4f59-98c1-7e366b0f4b0b-0.parquet
new file mode 100644
index 00000000..f31d5ecc
Binary files /dev/null and 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/bucket-0/data-334e3ff1-32c3-4f59-98c1-7e366b0f4b0b-0.parquet
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/manifest/manifest-37d9c141-c626-434c-bdc0-506cc6f3d04e-0
 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/manifest/manifest-37d9c141-c626-434c-bdc0-506cc6f3d04e-0
new file mode 100644
index 00000000..0d557ef9
Binary files /dev/null and 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/manifest/manifest-37d9c141-c626-434c-bdc0-506cc6f3d04e-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/manifest/manifest-list-edec5851-5eca-466b-9bde-780df761f932-0
 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/manifest/manifest-list-edec5851-5eca-466b-9bde-780df761f932-0
new file mode 100644
index 00000000..760835ce
Binary files /dev/null and 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/manifest/manifest-list-edec5851-5eca-466b-9bde-780df761f932-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/manifest/manifest-list-6e560f60-1b27-45ff-b676-0bb081ff3261-0
 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/manifest/manifest-list-edec5851-5eca-466b-9bde-780df761f932-1
similarity index 89%
rename from 
test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/manifest/manifest-list-6e560f60-1b27-45ff-b676-0bb081ff3261-0
rename to 
test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/manifest/manifest-list-edec5851-5eca-466b-9bde-780df761f932-1
index a01a7456..c025a6df 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/manifest/manifest-list-6e560f60-1b27-45ff-b676-0bb081ff3261-0
 and 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/manifest/manifest-list-edec5851-5eca-466b-9bde-780df761f932-1
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/schema/schema-0
 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/schema/schema-0
new file mode 100644
index 00000000..c090dea0
--- /dev/null
+++ 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/schema/schema-0
@@ -0,0 +1 @@
+{"version":3,"id":0,"fields":[{"id":0,"name":"id","type":"INT NOT 
NULL"},{"id":1,"name":"f_array_blob","type":{"type":"ARRAY","element":"BLOB"}}],"highestFieldId":1,"partitionKeys":[],"primaryKeys":[],"options":{"file.format":"parquet","manifest.format":"avro","data-evolution.enabled":"true","row-tracking.enabled":"true","bucket":"-1","blob-field":"f_array_blob"},"comment":null,"timeMillis":1788860192967}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/LATEST
 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/snapshot/LATEST
similarity index 100%
copy from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/LATEST
copy to 
test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/snapshot/LATEST
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/snapshot/snapshot-1
 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/snapshot/snapshot-1
new file mode 100644
index 00000000..51c4b5d9
--- /dev/null
+++ 
b/test/test_data/parquet/append_types_compatibility.db/rust_array_blob_types/snapshot/snapshot-1
@@ -0,0 +1 @@
+{"version":3,"id":1,"schemaId":0,"baseManifestList":"manifest-list-edec5851-5eca-466b-9bde-780df761f932-0","deltaManifestList":"manifest-list-edec5851-5eca-466b-9bde-780df761f932-1","commitUser":"a8381718-0f79-46d1-92ec-2879a4cca6f8","commitIdentifier":9223372036854775807,"commitKind":"APPEND","timeMillis":1788860192978,"totalRecordCount":3,"deltaRecordCount":3,"nextRowId":3}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/bucket-0/data-27af7a11-39ca-4131-b12d-81f824f57978-0.parquet
 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/bucket-0/data-27af7a11-39ca-4131-b12d-81f824f57978-0.parquet
new file mode 100644
index 00000000..08aadfe5
Binary files /dev/null and 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/bucket-0/data-27af7a11-39ca-4131-b12d-81f824f57978-0.parquet
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/manifest/manifest-30836888-cc9d-4ad0-b200-6da6e101ebd4-0
 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/manifest/manifest-30836888-cc9d-4ad0-b200-6da6e101ebd4-0
new file mode 100644
index 00000000..72f09905
Binary files /dev/null and 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/manifest/manifest-30836888-cc9d-4ad0-b200-6da6e101ebd4-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/manifest/manifest-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-0
 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/manifest/manifest-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-0
new file mode 100644
index 00000000..68de8b16
Binary files /dev/null and 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/manifest/manifest-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-0
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/manifest/manifest-list-6e560f60-1b27-45ff-b676-0bb081ff3261-1
 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/manifest/manifest-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-1
similarity index 89%
rename from 
test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/manifest/manifest-list-6e560f60-1b27-45ff-b676-0bb081ff3261-1
rename to 
test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/manifest/manifest-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-1
index 5478f74d..7050b7cf 100644
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/manifest/manifest-list-6e560f60-1b27-45ff-b676-0bb081ff3261-1
 and 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/manifest/manifest-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-1
 differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/schema/schema-0
 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/schema/schema-0
new file mode 100644
index 00000000..b6cd8894
--- /dev/null
+++ 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/schema/schema-0
@@ -0,0 +1 @@
+{"version":3,"id":0,"fields":[{"id":0,"name":"id","type":"INT NOT 
NULL"},{"id":1,"name":"f_map_blob","type":{"type":"MAP","key":"STRING NOT 
NULL","value":"BLOB"}}],"highestFieldId":1,"partitionKeys":[],"primaryKeys":[],"options":{"blob-field":"f_map_blob","row-tracking.enabled":"true","file.format":"parquet","manifest.format":"avro","data-evolution.enabled":"true","bucket":"-1"},"comment":null,"timeMillis":1788860192979}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/LATEST
 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/snapshot/LATEST
similarity index 100%
rename from 
test/test_data/parquet/append_types_compatibility.db/java_nested_blob_types/snapshot/LATEST
rename to 
test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/snapshot/LATEST
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/snapshot/snapshot-1
 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/snapshot/snapshot-1
new file mode 100644
index 00000000..d2926bd6
--- /dev/null
+++ 
b/test/test_data/parquet/append_types_compatibility.db/rust_map_blob_types/snapshot/snapshot-1
@@ -0,0 +1 @@
+{"version":3,"id":1,"schemaId":0,"baseManifestList":"manifest-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-0","deltaManifestList":"manifest-list-ea2b8a07-d439-44e6-86f2-451aa2dc1db6-1","commitUser":"cb443c72-2132-4223-9af0-f39360413a6a","commitIdentifier":9223372036854775807,"commitKind":"APPEND","timeMillis":1788860192988,"totalRecordCount":3,"deltaRecordCount":3,"nextRowId":3}
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/bucket-0/data-4749ab27-cf5e-48e5-8449-3f674480e077-0.parquet
 
b/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/bucket-0/data-4749ab27-cf5e-48e5-8449-3f674480e077-0.parquet
deleted file mode 100644
index 0383ee56..00000000
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/bucket-0/data-4749ab27-cf5e-48e5-8449-3f674480e077-0.parquet
 and /dev/null differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/manifest/manifest-0d1ac1c3-300f-45fe-ad80-45445dec4b45-0
 
b/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/manifest/manifest-0d1ac1c3-300f-45fe-ad80-45445dec4b45-0
deleted file mode 100644
index 286b07f3..00000000
Binary files 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/manifest/manifest-0d1ac1c3-300f-45fe-ad80-45445dec4b45-0
 and /dev/null differ
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/schema/schema-0
 
b/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/schema/schema-0
deleted file mode 100644
index 66ceca0a..00000000
--- 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/schema/schema-0
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"id":0,"fields":[{"id":0,"name":"id","type":"INT NOT 
NULL"},{"id":1,"name":"f_array_blob","type":{"type":"ARRAY","element":"BLOB"}},{"id":2,"name":"f_map_blob","type":{"type":"MAP","key":"STRING
 NOT 
NULL","value":"BLOB"}}],"highestFieldId":2,"partitionKeys":[],"primaryKeys":[],"options":{"manifest.format":"avro","file.format":"parquet","data-evolution.enabled":"true","bucket":"-1","row-tracking.enabled":"true","blob-field":"f_array_blob,f_map_blob"},"comment":null,"timeMilli
 [...]
\ No newline at end of file
diff --git 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/snapshot/snapshot-1
 
b/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/snapshot/snapshot-1
deleted file mode 100644
index c24110af..00000000
--- 
a/test/test_data/parquet/append_types_compatibility.db/rust_nested_blob_types/snapshot/snapshot-1
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"id":1,"schemaId":0,"baseManifestList":"manifest-list-6e560f60-1b27-45ff-b676-0bb081ff3261-0","deltaManifestList":"manifest-list-6e560f60-1b27-45ff-b676-0bb081ff3261-1","commitUser":"ce789997-7558-44de-8068-c5d7e744dba9","commitIdentifier":9223372036854775807,"commitKind":"APPEND","timeMillis":1788858155032,"totalRecordCount":3,"deltaRecordCount":3,"nextRowId":3}
\ No newline at end of file

Reply via email to