2626#include < arrow/util/decimal.h>
2727#include < avro/Generic.hh>
2828#include < avro/Node.hh>
29+ #include < avro/NodeImpl.hh>
2930#include < avro/Types.hh>
3031
3132#include " iceberg/arrow/arrow_error_transform_internal.h"
@@ -102,8 +103,7 @@ Status AppendListToBuilder(const ::avro::NodePtr& avro_node,
102103 const auto & avro_array = avro_datum.value <::avro::GenericArray>();
103104
104105 auto * list_builder = internal::checked_cast<::arrow::ListBuilder*>(array_builder);
105- ICEBERG_ARROW_RETURN_NOT_OK (list_builder->Append (
106- /* is_valid=*/ true , /* length=*/ static_cast <int64_t >(avro_array.value ().size ())));
106+ ICEBERG_ARROW_RETURN_NOT_OK (list_builder->Append ());
107107
108108 const auto & element_projection = projection.children [0 ];
109109 auto * value_builder = list_builder->value_builder ();
@@ -123,8 +123,65 @@ Status AppendMapToBuilder(const ::avro::NodePtr& avro_node,
123123 const FieldProjection& key_projection,
124124 const FieldProjection& value_projection,
125125 const MapType& map_type, ::arrow::ArrayBuilder* array_builder) {
126- // TODO(gangwu): support both regular map and array-based map.
127- return NotImplemented (" AppendMapToBuilder is not implemented" );
126+ auto * map_builder = internal::checked_cast<::arrow::MapBuilder*>(array_builder);
127+
128+ if (avro_node->type () == ::avro::AVRO_MAP) {
129+ // Handle regular Avro map: map<string, value>
130+ const auto & avro_map = avro_datum.value <::avro::GenericMap>();
131+ const auto & map_entries = avro_map.value ();
132+
133+ const auto & key_node = avro_node->leafAt (0 );
134+ const auto & value_node = avro_node->leafAt (1 );
135+
136+ const auto & key_field = map_type.key ();
137+ const auto & value_field = map_type.value ();
138+
139+ ICEBERG_ARROW_RETURN_NOT_OK (map_builder->Append ());
140+ auto * key_builder = map_builder->key_builder ();
141+ auto * item_builder = map_builder->item_builder ();
142+
143+ for (const auto & entry : map_entries) {
144+ ICEBERG_RETURN_UNEXPECTED (AppendFieldToBuilder (
145+ key_node, entry.first , key_projection, key_field, key_builder));
146+ ICEBERG_RETURN_UNEXPECTED (AppendFieldToBuilder (
147+ value_node, entry.second , value_projection, value_field, item_builder));
148+ }
149+
150+ return {};
151+ } else if (avro_node->type () == ::avro::AVRO_ARRAY && HasMapLogicalType (avro_node)) {
152+ // Handle array-based map: list<struct<key, value>>
153+ const auto & avro_array = avro_datum.value <::avro::GenericArray>();
154+ const auto & array_entries = avro_array.value ();
155+
156+ const auto & key_field = map_type.key ();
157+ const auto & value_field = map_type.value ();
158+
159+ ICEBERG_ARROW_RETURN_NOT_OK (map_builder->Append ());
160+ auto * key_builder = map_builder->key_builder ();
161+ auto * item_builder = map_builder->item_builder ();
162+
163+ const auto & record_node = avro_node->leafAt (0 );
164+ if (record_node->type () != ::avro::AVRO_RECORD || record_node->leaves () != 2 ) {
165+ return InvalidArgument (
166+ " Array-based map must contain records with exactly 2 fields, got: {}" ,
167+ ToString (record_node));
168+ }
169+ const auto & key_node = record_node->leafAt (0 );
170+ const auto & value_node = record_node->leafAt (1 );
171+
172+ for (const auto & entry : array_entries) {
173+ const auto & record = entry.value <::avro::GenericRecord>();
174+ ICEBERG_RETURN_UNEXPECTED (AppendFieldToBuilder (
175+ key_node, record.fieldAt (0 ), key_projection, key_field, key_builder));
176+ ICEBERG_RETURN_UNEXPECTED (AppendFieldToBuilder (
177+ value_node, record.fieldAt (1 ), value_projection, value_field, item_builder));
178+ }
179+
180+ return {};
181+ } else {
182+ return InvalidArgument (" Expected Avro map or array with map logical type, got: {}" ,
183+ ToString (avro_node));
184+ }
128185}
129186
130187// / \brief Append nested Avro data to Arrow array builder based on type.
0 commit comments