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

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


The following commit(s) were added to refs/heads/main by this push:
     new 48d0a5f1 feat(parquet): apply name mapping to files without field IDs 
(#1044)
48d0a5f1 is described below

commit 48d0a5f17c959f7f0290527992728280013bc241
Author: Haoxiang Meng <[email protected]>
AuthorDate: Thu Oct 22 14:56:40 2026 +0800

    feat(parquet): apply name mapping to files without field IDs (#1044)
    
    Co-authored-by: kiwimeng <[email protected]>
---
 src/iceberg/parquet/parquet_reader.cc              |  20 +--
 src/iceberg/parquet/parquet_schema_util.cc         |  55 ++++++++
 src/iceberg/parquet/parquet_schema_util_internal.h |  11 ++
 src/iceberg/test/parquet_schema_test.cc            | 153 +++++++++++++++++++++
 src/iceberg/test/parquet_test.cc                   | 130 +++++++++++++++++
 5 files changed, 361 insertions(+), 8 deletions(-)

diff --git a/src/iceberg/parquet/parquet_reader.cc 
b/src/iceberg/parquet/parquet_reader.cc
index 12da76a9..549a3d3a 100644
--- a/src/iceberg/parquet/parquet_reader.cc
+++ b/src/iceberg/parquet/parquet_reader.cc
@@ -61,19 +61,23 @@ Result<std::shared_ptr<::arrow::io::RandomAccessFile>> 
OpenInputStream(
 }
 
 Result<SchemaProjection> BuildProjection(::parquet::arrow::FileReader* reader,
-                                         const Schema& read_schema) {
+                                         const Schema& read_schema,
+                                         const NameMapping* name_mapping) {
   auto metadata = reader->parquet_reader()->metadata();
 
-  if (!HasFieldIds(metadata->schema()->schema_root())) {
-    // TODO(gangwu): apply name mapping to Parquet schema
-    return NotImplemented("Applying name mapping to Parquet schema is not 
implemented");
-  }
-
   ::parquet::arrow::SchemaManifest schema_manifest;
   ICEBERG_ARROW_RETURN_NOT_OK(::parquet::arrow::SchemaManifest::Make(
       metadata->schema(), metadata->key_value_metadata(), reader->properties(),
       &schema_manifest));
 
+  if (!HasFieldIds(metadata->schema()->schema_root())) {
+    if (name_mapping == nullptr) {
+      return InvalidSchema(
+          "Parquet file schema has no field IDs and no name mapping provided");
+    }
+    ApplyNameMapping(*name_mapping, &schema_manifest);
+  }
+
   // Leverage SchemaManifest to project the schema
   ICEBERG_ASSIGN_OR_RAISE(auto projection, Project(read_schema, 
schema_manifest));
   return projection;
@@ -269,7 +273,6 @@ struct ReadContext {
 // 1. Make the memory pool configurable
 // 2. Catch ParquetException and convert to Status/Result
 // 3. Add utility to convert Arrow Status/Result to Iceberg Status/Result
-// 4. Check field ids and apply name mapping if needed
 class ParquetReader::Impl {
  public:
   // Open the Parquet reader with the given options
@@ -301,7 +304,8 @@ class ParquetReader::Impl {
     ICEBERG_RETURN_UNEXPECTED(PlanRowGroups(options));
 
     // Project read schema onto the Parquet file schema
-    ICEBERG_ASSIGN_OR_RAISE(projection_, BuildProjection(reader_.get(), 
*read_schema_));
+    ICEBERG_ASSIGN_OR_RAISE(projection_, BuildProjection(reader_.get(), 
*read_schema_,
+                                                         
options.name_mapping.get()));
     metadata_context_ = {.file_path = options.path,
                          .first_row_id = options.first_row_id,
                          .data_sequence_number = options.data_sequence_number};
diff --git a/src/iceberg/parquet/parquet_schema_util.cc 
b/src/iceberg/parquet/parquet_schema_util.cc
index 05f038c1..6a15659e 100644
--- a/src/iceberg/parquet/parquet_schema_util.cc
+++ b/src/iceberg/parquet/parquet_schema_util.cc
@@ -26,6 +26,7 @@
 
 #include "iceberg/constants.h"
 #include "iceberg/metadata_columns.h"
+#include "iceberg/name_mapping.h"
 #include "iceberg/parquet/parquet_schema_util_internal.h"
 #include "iceberg/result.h"
 #include "iceberg/schema_internal.h"
@@ -716,6 +717,52 @@ void CollectColumnIds(const FieldProjection& 
field_projection,
   }
 }
 
+// Forward declaration
+void ApplyNameMappingToField(const NameMapping& mapping, 
std::vector<std::string>& names,
+                             ::parquet::arrow::SchemaField* parquet_field);
+
+void ApplyNameMappingToChild(const NameMapping& mapping, 
std::vector<std::string>& names,
+                             std::string name, ::parquet::arrow::SchemaField* 
child) {
+  names.push_back(std::move(name));
+  ApplyNameMappingToField(mapping, names, child);
+  names.pop_back();
+}
+
+void ApplyNameMappingToField(const NameMapping& mapping, 
std::vector<std::string>& names,
+                             ::parquet::arrow::SchemaField* parquet_field) {
+  if (auto mapped_field = mapping.Find(names);
+      mapped_field.has_value() && mapped_field->get().field_id.has_value()) {
+    parquet_field->field =
+        parquet_field->field->WithMergedMetadata(::arrow::key_value_metadata(
+            {std::string(kParquetFieldIdKey)},
+            {std::to_string(mapped_field->get().field_id.value())}));
+  }
+
+  auto& children = parquet_field->children;
+  switch (parquet_field->field->type()->id()) {
+    case ::arrow::Type::STRUCT:
+      for (auto& child : children) {
+        ApplyNameMappingToChild(mapping, names, child.field->name(), &child);
+      }
+      break;
+    case ::arrow::Type::LIST:
+    case ::arrow::Type::LARGE_LIST:
+      if (children.size() == 1) {
+        ApplyNameMappingToChild(mapping, names, "element", &children[0]);
+      }
+      break;
+    case ::arrow::Type::MAP:
+      // The key and value sit under the entries struct, which the name path 
skips.
+      if (children.size() == 1 && children[0].children.size() == 2) {
+        ApplyNameMappingToChild(mapping, names, "key", 
&children[0].children[0]);
+        ApplyNameMappingToChild(mapping, names, "value", 
&children[0].children[1]);
+      }
+      break;
+    default:
+      break;
+  }
+}
+
 }  // namespace
 
 Result<SchemaProjection> Project(const Schema& expected_schema,
@@ -753,6 +800,14 @@ bool HasFieldIds(const ::parquet::schema::NodePtr& node) {
   return false;
 }
 
+void ApplyNameMapping(const NameMapping& mapping,
+                      ::parquet::arrow::SchemaManifest* parquet_schema) {
+  std::vector<std::string> names;
+  for (auto& parquet_field : parquet_schema->schema_fields) {
+    ApplyNameMappingToChild(mapping, names, parquet_field.field->name(), 
&parquet_field);
+  }
+}
+
 Result<std::shared_ptr<::parquet::SchemaDescriptor>> ToParquetSchema(
     const Schema& iceberg_schema) {
   ::parquet::schema::NodeVector fields;
diff --git a/src/iceberg/parquet/parquet_schema_util_internal.h 
b/src/iceberg/parquet/parquet_schema_util_internal.h
index 2eafd013..6612de05 100644
--- a/src/iceberg/parquet/parquet_schema_util_internal.h
+++ b/src/iceberg/parquet/parquet_schema_util_internal.h
@@ -26,6 +26,7 @@
 
 #include "iceberg/schema.h"
 #include "iceberg/schema_util.h"
+#include "iceberg/type_fwd.h"
 
 namespace iceberg::parquet {
 
@@ -63,6 +64,16 @@ std::vector<int32_t> SelectedColumnIndices(const 
SchemaProjection& projection);
 /// \return True if the Parquet schema has field IDs, false otherwise.
 bool HasFieldIds(const ::parquet::schema::NodePtr& root_node);
 
+/// \brief Assign field IDs from a name mapping to a Parquet schema without 
field IDs.
+///
+/// List elements use "element"; map keys and values use "key" and "value".
+/// Fields without a mapped ID remain unassigned.
+///
+/// \param mapping The name mapping to apply.
+/// \param parquet_schema The Parquet schema to update in place.
+void ApplyNameMapping(const NameMapping& mapping,
+                      ::parquet::arrow::SchemaManifest* parquet_schema);
+
 /// \brief Convert an Iceberg schema to a Parquet schema.
 Result<std::shared_ptr<::parquet::SchemaDescriptor>> ToParquetSchema(
     const Schema& iceberg_schema);
diff --git a/src/iceberg/test/parquet_schema_test.cc 
b/src/iceberg/test/parquet_schema_test.cc
index 4663b184..4cf36da1 100644
--- a/src/iceberg/test/parquet_schema_test.cc
+++ b/src/iceberg/test/parquet_schema_test.cc
@@ -31,6 +31,7 @@
 #include <parquet/types.h>
 
 #include "iceberg/metadata_columns.h"
+#include "iceberg/name_mapping.h"
 #include "iceberg/parquet/parquet_schema_util_internal.h"
 #include "iceberg/schema.h"
 #include "iceberg/test/matchers.h"
@@ -203,6 +204,158 @@ TEST(HasFieldIdsTest, GroupNode) {
 }
 // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
 
+TEST(ParquetNameMappingTest, ApplyNameMapping) {
+  Schema expected_schema({
+      SchemaField::MakeOptional(/*field_id=*/1, "id", int64()),
+      SchemaField::MakeOptional(/*field_id=*/2, "name", string()),
+  });
+
+  auto parquet_schema =
+      MakeGroupNode("iceberg_schema", {MakeInt64Node("id"), 
MakeStringNode("name")});
+  auto schema_manifest = MakeSchemaManifest(parquet_schema);
+  auto mapping = NameMapping::Make({
+      MappedField{.names = {"id"}, .field_id = 1},
+      MappedField{.names = {"name"}, .field_id = 2},
+  });
+
+  ApplyNameMapping(*mapping, &schema_manifest);
+  EXPECT_EQ(schema_manifest.schema_fields[0].field->metadata()->Get(
+                std::string(kParquetFieldIdKey)),
+            "1");
+  EXPECT_EQ(schema_manifest.schema_fields[1].field->metadata()->Get(
+                std::string(kParquetFieldIdKey)),
+            "2");
+
+  auto projection_result = Project(expected_schema, schema_manifest);
+  ASSERT_THAT(projection_result, IsOk());
+
+  const auto& projection = *projection_result;
+  ASSERT_EQ(projection.fields.size(), 2);
+  ASSERT_PROJECTED_FIELD(projection.fields[0], 0);
+  ASSERT_PROJECTED_FIELD(projection.fields[1], 1);
+  ASSERT_EQ(SelectedColumnIndices(projection), std::vector<int32_t>({0, 1}));
+}
+
+TEST(ParquetNameMappingTest, ApplyNameMappingWithAlias) {
+  Schema expected_schema({
+      SchemaField::MakeOptional(/*field_id=*/2, "full_name", string()),
+  });
+
+  auto parquet_schema = MakeGroupNode("iceberg_schema", 
{MakeStringNode("user_name")});
+  auto schema_manifest = MakeSchemaManifest(parquet_schema);
+  auto mapping = NameMapping::Make({
+      MappedField{.names = {"full_name", "user_name"}, .field_id = 2},
+  });
+
+  ApplyNameMapping(*mapping, &schema_manifest);
+  auto projection_result = Project(expected_schema, schema_manifest);
+  ASSERT_THAT(projection_result, IsOk());
+
+  const auto& projection = *projection_result;
+  ASSERT_EQ(projection.fields.size(), 1);
+  ASSERT_PROJECTED_FIELD(projection.fields[0], 0);
+}
+
+TEST(ParquetNameMappingTest, ApplyNameMappingToNestedFields) {
+  Schema expected_schema({
+      SchemaField::MakeOptional(
+          /*field_id=*/1, "profile",
+          std::make_shared<StructType>(std::vector<SchemaField>{
+              SchemaField::MakeOptional(/*field_id=*/101, "name", string()),
+          })),
+      SchemaField::MakeOptional(
+          /*field_id=*/2, "numbers",
+          std::make_shared<ListType>(SchemaField::MakeOptional(
+              /*field_id=*/201, "element", int32()))),
+      SchemaField::MakeOptional(
+          /*field_id=*/3, "counts",
+          std::make_shared<MapType>(
+              SchemaField::MakeRequired(/*field_id=*/301, "key", string()),
+              SchemaField::MakeOptional(/*field_id=*/302, "value", int32()))),
+  });
+
+  auto parquet_schema = MakeGroupNode(
+      "iceberg_schema",
+      {
+          MakeGroupNode("profile", {MakeStringNode("name")}),
+          MakeListNode("numbers", MakeInt32Node("item")),
+          MakeMapNode("counts",
+                      MakeStringNode("key", /*field_id=*/-1, 
/*optional=*/false),
+                      MakeInt32Node("value")),
+      });
+  auto mapping = NameMapping::Make({
+      MappedField{.names = {"profile"},
+                  .field_id = 1,
+                  .nested_mapping = MappedFields::Make({
+                      MappedField{.names = {"name"}, .field_id = 101},
+                  })},
+      MappedField{.names = {"numbers"},
+                  .field_id = 2,
+                  .nested_mapping = MappedFields::Make({
+                      MappedField{.names = {"element"}, .field_id = 201},
+                  })},
+      MappedField{.names = {"counts"},
+                  .field_id = 3,
+                  .nested_mapping = MappedFields::Make({
+                      MappedField{.names = {"key"}, .field_id = 301},
+                      MappedField{.names = {"value"}, .field_id = 302},
+                  })},
+  });
+
+  auto schema_manifest = MakeSchemaManifest(parquet_schema);
+  ApplyNameMapping(*mapping, &schema_manifest);
+  auto projection_result = Project(expected_schema, schema_manifest);
+  ASSERT_THAT(projection_result, IsOk());
+
+  const auto& projection = *projection_result;
+  ASSERT_EQ(projection.fields.size(), 3);
+  ASSERT_PROJECTED_FIELD(projection.fields[0], 0);
+  ASSERT_EQ(projection.fields[0].children.size(), 1);
+  ASSERT_PROJECTED_FIELD(projection.fields[0].children[0], 0);
+  ASSERT_PROJECTED_FIELD(projection.fields[1], 1);
+  ASSERT_EQ(projection.fields[1].children.size(), 1);
+  ASSERT_PROJECTED_FIELD(projection.fields[1].children[0], 0);
+  ASSERT_PROJECTED_FIELD(projection.fields[2], 2);
+  ASSERT_EQ(projection.fields[2].children.size(), 2);
+  ASSERT_PROJECTED_FIELD(projection.fields[2].children[0], 0);
+  ASSERT_PROJECTED_FIELD(projection.fields[2].children[1], 1);
+  ASSERT_EQ(SelectedColumnIndices(projection), std::vector<int32_t>({0, 1, 2, 
3}));
+}
+
+TEST(ParquetNameMappingTest, ApplyNameMappingWithUnmappedFields) {
+  Schema expected_schema({
+      SchemaField::MakeOptional(/*field_id=*/1, "id", int64()),
+      SchemaField::MakeOptional(/*field_id=*/2, "extra", string()),
+      SchemaField::MakeOptional(/*field_id=*/3, "unassigned", string()),
+  });
+
+  auto parquet_schema = MakeGroupNode(
+      "iceberg_schema",
+      {MakeInt64Node("id"), MakeStringNode("extra"), 
MakeStringNode("unassigned")});
+  auto schema_manifest = MakeSchemaManifest(parquet_schema);
+  auto mapping = NameMapping::Make({
+      MappedField{.names = {"id"}, .field_id = 1},
+      MappedField{.names = {"unassigned"}, .field_id = std::nullopt},
+  });
+
+  ApplyNameMapping(*mapping, &schema_manifest);
+  for (size_t i = 1; i < schema_manifest.schema_fields.size(); ++i) {
+    const auto& metadata = schema_manifest.schema_fields[i].field->metadata();
+    EXPECT_TRUE(metadata == nullptr ||
+                metadata->FindKey(std::string(kParquetFieldIdKey)) < 0);
+  }
+
+  auto projection_result = Project(expected_schema, schema_manifest);
+  ASSERT_THAT(projection_result, IsOk());
+
+  const auto& projection = *projection_result;
+  ASSERT_EQ(projection.fields.size(), 3);
+  ASSERT_PROJECTED_FIELD(projection.fields[0], 0);
+  ASSERT_PROJECTED_NULL_FIELD(projection.fields[1]);
+  ASSERT_PROJECTED_NULL_FIELD(projection.fields[2]);
+  ASSERT_EQ(SelectedColumnIndices(projection), std::vector<int32_t>({0}));
+}
+
 TEST(ParquetSchemaProjectionTest, ProjectIdenticalSchemas) {
   Schema expected_schema({
       SchemaField::MakeRequired(/*field_id=*/1, "id", iceberg::int64()),
diff --git a/src/iceberg/test/parquet_test.cc b/src/iceberg/test/parquet_test.cc
index 1327f03f..c35ba447 100644
--- a/src/iceberg/test/parquet_test.cc
+++ b/src/iceberg/test/parquet_test.cc
@@ -49,6 +49,7 @@
 #include "iceberg/file_reader.h"
 #include "iceberg/file_writer.h"
 #include "iceberg/metadata_columns.h"
+#include "iceberg/name_mapping.h"
 #include "iceberg/parquet/parquet_data_util_internal.h"
 #include "iceberg/parquet/parquet_register.h"
 #include "iceberg/result.h"
@@ -267,6 +268,20 @@ class ParquetReaderTest : public TempFileTestBase {
                                    .properties = 
std::move(writer_properties)}));
   }
 
+  void CreateParquetFileWithoutFieldIds(const 
std::shared_ptr<::arrow::DataType>& type,
+                                        std::string_view json) {
+    auto array = ::arrow::json::ArrayFromJSONString(type, json).ValueOrDie();
+    auto batch = ::arrow::RecordBatch::FromStructArray(array).ValueOrDie();
+    auto table = ::arrow::Table::FromRecordBatches({batch}).ValueOrDie();
+    auto io = internal::checked_cast<arrow::ArrowFileSystemFileIO&>(*file_io_);
+    auto outfile = io.fs()->OpenOutputStream(temp_parquet_file_).ValueOrDie();
+
+    ASSERT_TRUE(::parquet::arrow::WriteTable(*table, 
::arrow::default_memory_pool(),
+                                             outfile, table->num_rows())
+                    .ok());
+    ASSERT_TRUE(outfile->Close().ok());
+  }
+
   void CreateListParquetFile() {
     auto schema = std::make_shared<Schema>(std::vector<SchemaField>{
         SchemaField::MakeRequired(1, "id", int32()),
@@ -556,6 +571,121 @@ TEST_F(ParquetReaderTest, ReadTwoFields) {
   ASSERT_NO_FATAL_FAILURE(VerifyExhausted(*reader));
 }
 
+TEST_F(ParquetReaderTest, ReadWithoutFieldIdsOrNameMapping) {
+  ASSERT_NO_FATAL_FAILURE(CreateParquetFileWithoutFieldIds(
+      ::arrow::struct_({::arrow::field("id", ::arrow::int32())}), R"([[1]])"));
+  auto schema = std::make_shared<Schema>(
+      std::vector<SchemaField>{SchemaField::MakeOptional(1, "id", int32())});
+
+  auto reader_result = ReaderFactoryRegistry::Open(
+      FileFormatType::kParquet,
+      {.path = temp_parquet_file_, .io = file_io_, .projection = schema});
+  ASSERT_THAT(reader_result, IsError(ErrorKind::kInvalidSchema));
+  EXPECT_THAT(reader_result, HasErrorMessage("no field IDs and no name 
mapping"));
+}
+
+TEST_F(ParquetReaderTest, ReadWithNameMapping) {
+  ASSERT_NO_FATAL_FAILURE(CreateParquetFileWithoutFieldIds(
+      ::arrow::struct_({::arrow::field("id", ::arrow::int32()),
+                        ::arrow::field("name", ::arrow::utf8())}),
+      R"([[1, "Foo"], [2, "Bar"], [3, "Baz"]])"));
+  auto schema = std::make_shared<Schema>(std::vector<SchemaField>{
+      SchemaField::MakeOptional(2, "full_name", string()),
+      SchemaField::MakeOptional(1, "id", int64()),
+      SchemaField::MakeOptional(3, "extra", string()),
+  });
+  auto mapping = NameMapping::Make({
+      MappedField{.names = {"id"}, .field_id = 1},
+      MappedField{.names = {"name", "full_name"}, .field_id = 2},
+  });
+
+  ICEBERG_UNWRAP_OR_FAIL(
+      auto reader, ReaderFactoryRegistry::Open(FileFormatType::kParquet,
+                                               {.path = temp_parquet_file_,
+                                                .io = file_io_,
+                                                .projection = schema,
+                                                .name_mapping = 
std::move(mapping)}));
+  ASSERT_NO_FATAL_FAILURE(VerifyNextBatch(
+      *reader, R"([["Foo", 1, null], ["Bar", 2, null], ["Baz", 3, null]])"));
+  ASSERT_NO_FATAL_FAILURE(VerifyExhausted(*reader));
+}
+
+TEST_F(ParquetReaderTest, ReadWithFieldIdsAndNameMapping) {
+  CreateSimpleParquetFile();
+  auto schema = std::make_shared<Schema>(std::vector<SchemaField>{
+      SchemaField::MakeRequired(1, "id", int32()),
+      SchemaField::MakeOptional(2, "name", string()),
+  });
+  auto mapping = NameMapping::Make({
+      MappedField{.names = {"id"}, .field_id = 2},
+      MappedField{.names = {"name"}, .field_id = 1},
+  });
+
+  ICEBERG_UNWRAP_OR_FAIL(
+      auto reader, ReaderFactoryRegistry::Open(FileFormatType::kParquet,
+                                               {.path = temp_parquet_file_,
+                                                .io = file_io_,
+                                                .projection = schema,
+                                                .name_mapping = 
std::move(mapping)}));
+  ASSERT_NO_FATAL_FAILURE(
+      VerifyNextBatch(*reader, R"([[1, "Foo"], [2, "Bar"], [3, "Baz"]])"));
+  ASSERT_NO_FATAL_FAILURE(VerifyExhausted(*reader));
+}
+
+TEST_F(ParquetReaderTest, ReadNestedFieldsWithNameMapping) {
+  auto value_type = ::arrow::struct_({::arrow::field("value", 
::arrow::int32())});
+  auto type = ::arrow::struct_({
+      ::arrow::field("profile", value_type),
+      ::arrow::field("numbers", ::arrow::list(::arrow::field("item", 
value_type))),
+      ::arrow::field("counts", ::arrow::map(::arrow::utf8(), value_type)),
+  });
+  const std::string json = R"([
+    [[1], [[2], null], [["a", [3]], ["b", null]]],
+    [null, [], []],
+    [[null], null, null]
+  ])";
+  ASSERT_NO_FATAL_FAILURE(CreateParquetFileWithoutFieldIds(type, json));
+
+  auto schema = std::make_shared<Schema>(std::vector<SchemaField>{
+      SchemaField::MakeOptional(1, "profile",
+                                
std::make_shared<StructType>(std::vector<SchemaField>{
+                                    SchemaField::MakeOptional(101, "value", 
int64()),
+                                })),
+      SchemaField::MakeOptional(2, "numbers",
+                                
std::make_shared<ListType>(SchemaField::MakeOptional(
+                                    201, "element",
+                                    
std::make_shared<StructType>(std::vector<SchemaField>{
+                                        SchemaField::MakeOptional(202, 
"value", int64()),
+                                    })))),
+      SchemaField::MakeOptional(
+          3, "counts",
+          std::make_shared<MapType>(
+              SchemaField::MakeRequired(301, "key", string()),
+              SchemaField::MakeOptional(
+                  302, "value",
+                  std::make_shared<StructType>(std::vector<SchemaField>{
+                      SchemaField::MakeOptional(303, "value", int64()),
+                  })))),
+  });
+  ICEBERG_UNWRAP_OR_FAIL(auto unique_mapping, CreateMapping(*schema));
+  std::shared_ptr<NameMapping> mapping = std::move(unique_mapping);
+
+  for (bool use_large_list : {false, true}) {
+    SCOPED_TRACE(use_large_list);
+    ReaderProperties properties;
+    properties.Set(ReaderProperties::kArrowUseLargeList, use_large_list);
+    ICEBERG_UNWRAP_OR_FAIL(
+        auto reader, ReaderFactoryRegistry::Open(FileFormatType::kParquet,
+                                                 {.path = temp_parquet_file_,
+                                                  .io = file_io_,
+                                                  .projection = schema,
+                                                  .name_mapping = mapping,
+                                                  .properties = 
std::move(properties)}));
+    ASSERT_NO_FATAL_FAILURE(VerifyNextBatch(*reader, json));
+    ASSERT_NO_FATAL_FAILURE(VerifyExhausted(*reader));
+  }
+}
+
 TEST_F(ParquetReaderTest, ReadMissingFieldsWithDefaults) {
   // The file contains only fields 1 and 2; the projected schema adds fields 3 
and 4
   // with initial-defaults, which are filled for all rows written before the 
columns

Reply via email to