@@ -323,6 +323,158 @@ class CreateMappingVisitor {
323323 }
324324};
325325
326+ // Visitor class for updating name mappings with schema changes
327+ class UpdateMappingVisitor {
328+ public:
329+ UpdateMappingVisitor (const std::map<int32_t , SchemaField>& updates,
330+ const std::multimap<int32_t , int32_t >& adds)
331+ : updates_(updates), adds_(adds) {}
332+
333+ Result<std::unique_ptr<MappedFields>> VisitMapping (const NameMapping& mapping) {
334+ auto fields_result = VisitFields (mapping.AsMappedFields ());
335+ ICEBERG_RETURN_UNEXPECTED (fields_result);
336+ return AddNewFields (std::move (*fields_result),
337+ -1 /* parent ID for top-level fields */ );
338+ }
339+
340+ private:
341+ Result<std::unique_ptr<MappedFields>> VisitFields (const MappedFields& fields) {
342+ // Recursively visit all fields
343+ std::vector<MappedField> field_results;
344+ field_results.reserve (fields.Size ());
345+
346+ for (const auto & field : fields.fields ()) {
347+ auto field_result = VisitField (field);
348+ ICEBERG_RETURN_UNEXPECTED (field_result);
349+ field_results.push_back (std::move (*field_result));
350+ }
351+
352+ // Build update assignments map for removing reassigned names
353+ std::unordered_map<std::string, int32_t > update_assignments;
354+ std::ranges::for_each (field_results, [&](const auto & field) {
355+ if (field.field_id .has_value ()) {
356+ auto update_it = updates_.find (field.field_id .value ());
357+ if (update_it != updates_.end ()) {
358+ update_assignments.emplace (std::string (update_it->second .name ()),
359+ field.field_id .value ());
360+ }
361+ }
362+ });
363+
364+ // Remove reassigned names from all fields
365+ for (auto & field : field_results) {
366+ field = RemoveReassignedNames (field, update_assignments);
367+ }
368+
369+ return MappedFields::Make (std::move (field_results));
370+ }
371+
372+ Result<MappedField> VisitField (const MappedField& field) {
373+ // Update this field's names
374+ std::unordered_set<std::string> field_names = field.names ;
375+ if (field.field_id .has_value ()) {
376+ auto update_it = updates_.find (field.field_id .value ());
377+ if (update_it != updates_.end ()) {
378+ field_names.insert (std::string (update_it->second .name ()));
379+ }
380+ }
381+
382+ std::unique_ptr<MappedFields> nested_mapping = nullptr ;
383+ if (field.nested_mapping != nullptr ) {
384+ auto nested_result = VisitFields (*field.nested_mapping );
385+ ICEBERG_RETURN_UNEXPECTED (nested_result);
386+ nested_mapping = std::move (*nested_result);
387+ }
388+
389+ // Add a new mapping for any new nested fields
390+ if (field.field_id .has_value ()) {
391+ auto nested_result =
392+ AddNewFields (std::move (nested_mapping), field.field_id .value ());
393+ ICEBERG_RETURN_UNEXPECTED (nested_result);
394+ nested_mapping = std::move (*nested_result);
395+ }
396+
397+ return MappedField{
398+ .names = std::move (field_names),
399+ .field_id = field.field_id ,
400+ .nested_mapping = std::move (nested_mapping),
401+ };
402+ }
403+
404+ Result<std::unique_ptr<MappedFields>> AddNewFields (
405+ std::unique_ptr<MappedFields> mapping, int32_t parent_id) {
406+ auto range = adds_.equal_range (parent_id);
407+ std::vector<const SchemaField*> fields_to_add;
408+ for (auto it = range.first ; it != range.second ; ++it) {
409+ auto update_it = updates_.find (it->second );
410+ if (update_it != updates_.end ()) {
411+ fields_to_add.push_back (&update_it->second );
412+ }
413+ }
414+
415+ if (fields_to_add.empty ()) {
416+ return std::move (mapping);
417+ }
418+
419+ std::vector<MappedField> new_fields;
420+ CreateMappingVisitor create_visitor;
421+ for (const auto * field_to_add : fields_to_add) {
422+ auto nested_result = VisitType (
423+ *field_to_add->type (),
424+ [&create_visitor](const auto & type) { return create_visitor.Visit (type); });
425+ ICEBERG_RETURN_UNEXPECTED (nested_result);
426+
427+ new_fields.emplace_back (MappedField{
428+ .names = {std::string (field_to_add->name ())},
429+ .field_id = field_to_add->field_id (),
430+ .nested_mapping = std::move (*nested_result),
431+ });
432+ }
433+
434+ if (mapping == nullptr || mapping->Size () == 0 ) {
435+ return MappedFields::Make (std::move (new_fields));
436+ }
437+
438+ // Build assignments map for removing reassigned names
439+ std::unordered_map<std::string, int32_t > assignments;
440+ for (const auto * field_to_add : fields_to_add) {
441+ assignments.emplace (std::string (field_to_add->name ()), field_to_add->field_id ());
442+ }
443+
444+ // create a copy of fields that can be updated (append new fields, replace existing
445+ // for reassignment)
446+ std::vector<MappedField> fields;
447+ fields.reserve (mapping->Size () + new_fields.size ());
448+ for (const auto & field : mapping->fields ()) {
449+ fields.push_back (RemoveReassignedNames (field, assignments));
450+ }
451+
452+ fields.insert (fields.end (), std::make_move_iterator (new_fields.begin ()),
453+ std::make_move_iterator (new_fields.end ()));
454+
455+ return MappedFields::Make (std::move (fields));
456+ }
457+
458+ static MappedField RemoveReassignedNames (
459+ const MappedField& field,
460+ const std::unordered_map<std::string, int32_t >& assignments) {
461+ std::unordered_set<std::string> updated_names = field.names ;
462+ std::erase_if (updated_names, [&](const std::string& name) {
463+ auto assign_it = assignments.find (name);
464+ return assign_it != assignments.end () &&
465+ (!field.field_id .has_value () || assign_it->second != field.field_id .value ());
466+ });
467+ return MappedField{
468+ .names = std::move (updated_names),
469+ .field_id = field.field_id ,
470+ .nested_mapping = field.nested_mapping ,
471+ };
472+ }
473+
474+ const std::map<int32_t , SchemaField>& updates_;
475+ const std::multimap<int32_t , int32_t >& adds_;
476+ };
477+
326478} // namespace
327479
328480Result<std::unique_ptr<NameMapping>> CreateMapping (const Schema& schema) {
@@ -335,4 +487,13 @@ Result<std::unique_ptr<NameMapping>> CreateMapping(const Schema& schema) {
335487 return NameMapping::Make (std::move (*result));
336488}
337489
490+ Result<std::unique_ptr<NameMapping>> UpdateMapping (
491+ const NameMapping& mapping, const std::map<int32_t , SchemaField>& updates,
492+ const std::multimap<int32_t , int32_t >& adds) {
493+ UpdateMappingVisitor visitor (updates, adds);
494+ auto result = visitor.VisitMapping (mapping);
495+ ICEBERG_RETURN_UNEXPECTED (result);
496+ return NameMapping::Make (std::move (*result));
497+ }
498+
338499} // namespace iceberg
0 commit comments