@@ -90,14 +90,9 @@ class UpdateSchema(UpdateTableMetadata["UpdateSchema"]):
9090 _allow_incompatible_changes : bool
9191 _case_sensitive : bool
9292
93- # Store user intent for retry support
94- _column_additions : list [tuple [str | tuple [str , ...], IcebergType , str | None , bool , Any ]]
95- _column_updates : list [tuple [str | tuple [str , ...], IcebergType | None , bool | None , str | None ]]
96- _column_deletions : list [str | tuple [str , ...]]
97- _column_renames : list [tuple [str | tuple [str , ...], str ]]
98- _move_operations : list [tuple [str , str | tuple [str , ...], str | tuple [str , ...] | None ]]
99- _optional_columns : list [str | tuple [str , ...]]
100- _default_value_updates : list [tuple [str | tuple [str , ...], Any ]]
93+ # Store all operations and order for retry support.
94+ # This _operations overlaps _adds, _updates and other intermediate variables.
95+ _operations : list [tuple [Any , ...]]
10196
10297 def __init__ (
10398 self ,
@@ -113,18 +108,8 @@ def __init__(
113108 self ._case_sensitive = case_sensitive
114109 self ._name_mapping = name_mapping
115110 self ._provided_schema = schema # Store for _reset_state
111+ self ._operations = [] # for retry support
116112
117- # Initialize user intent storage
118- self ._column_additions = []
119- self ._column_updates = []
120- self ._column_deletions = []
121- self ._column_renames = []
122- self ._move_operations = []
123- self ._optional_columns = []
124- self ._default_value_updates = []
125- self ._identifier_field_updates : set [str ] | None = None
126-
127- # Initialize state from metadata
128113 self ._init_state_from_metadata (schema )
129114
130115 def _init_state_from_metadata (self , schema : Schema | None = None ) -> None :
@@ -155,38 +140,48 @@ def get_column_name(field_id: int) -> str:
155140 }
156141
157142 def _reset_state (self ) -> None :
158- """Reset state for retry, rebuilding from refreshed metadata."""
159- self ._init_state_from_metadata (self ._provided_schema )
160-
161- for path , field_type , doc , required , default_value in self ._column_additions :
162- self ._do_add_column (path , field_type , doc , required , default_value )
163-
164- for path in self ._column_deletions :
165- self ._do_delete_column (path )
166-
167- for path_from , new_name in self ._column_renames :
168- self ._do_rename_column (path_from , new_name )
169-
170- for upd_path , upd_field_type , upd_required , upd_doc in self ._column_updates :
171- self ._do_update_column (upd_path , upd_field_type , upd_required , upd_doc )
143+ """Reset state for retry, rebuilding from refreshed metadata.
172144
173- for path in self ._optional_columns :
174- self ._set_column_requirement (path , required = False )
175-
176- for path , default_value in self ._default_value_updates :
177- self ._set_column_default_value (path , default_value )
145+ This is called on transaction retry to reapply the schema changes on top of the refreshed table metadata.
146+ """
147+ self ._init_state_from_metadata (self ._provided_schema )
178148
179- for op , path , other_path in self ._move_operations :
180- if op == "first" :
149+ # Refresh name mapping from the latest table metadata to avoid overwriting concurrent changes
150+ if self ._name_mapping is not None :
151+ self ._name_mapping = self ._transaction .table_metadata .name_mapping ()
152+
153+ for operation in self ._operations :
154+ op_type = operation [0 ]
155+ if op_type == "add" :
156+ _ , path , field_type , doc , required , default_value = operation
157+ self ._do_add_column (path , field_type , doc , required , default_value )
158+ elif op_type == "delete" :
159+ _ , path = operation
160+ self ._do_delete_column (path )
161+ elif op_type == "rename" :
162+ _ , path_from , new_name = operation
163+ self ._do_rename_column (path_from , new_name )
164+ elif op_type == "update" :
165+ _ , path , field_type , required , doc = operation
166+ self ._do_update_column (path , field_type , required , doc )
167+ elif op_type == "optional" :
168+ _ , path = operation
169+ self ._set_column_requirement (path , required = False )
170+ elif op_type == "default_value" :
171+ _ , path , default_value = operation
172+ self ._set_column_default_value (path , default_value )
173+ elif op_type == "move_first" :
174+ _ , path = operation
181175 self ._do_move_first (path )
182- elif op == "before" :
183- self ._do_move_before (path , other_path ) # type: ignore
184- elif op == "after" :
185- self ._do_move_after (path , other_path ) # type: ignore
186-
187- # Restore identifier fields if they were explicitly set
188- if self ._identifier_field_updates is not None :
189- self ._identifier_field_names = self ._identifier_field_updates .copy ()
176+ elif op_type == "move_before" :
177+ _ , path , before_path = operation
178+ self ._do_move_before (path , before_path )
179+ elif op_type == "move_after" :
180+ _ , path , after_name = operation
181+ self ._do_move_after (path , after_name )
182+ elif op_type == "set_identifier_fields" :
183+ _ , fields = operation
184+ self ._identifier_field_names = set (fields )
190185
191186 def case_sensitive (self , case_sensitive : bool ) -> UpdateSchema :
192187 """Determine if the case of schema needs to be considered when comparing column names.
@@ -243,7 +238,7 @@ def add_column(
243238 Returns:
244239 This for method chaining.
245240 """
246- self ._column_additions .append ((path , field_type , doc , required , default_value ))
241+ self ._operations .append (("add" , path , field_type , doc , required , default_value ))
247242 self ._do_add_column (path , field_type , doc , required , default_value )
248243 return self
249244
@@ -335,7 +330,7 @@ def delete_column(self, path: str | tuple[str, ...]) -> UpdateSchema:
335330 Returns:
336331 The UpdateSchema with the delete operation staged.
337332 """
338- self ._column_deletions .append (path )
333+ self ._operations .append (( "delete" , path ) )
339334 self ._do_delete_column (path )
340335 return self
341336
@@ -362,7 +357,7 @@ def set_default_value(self, path: str | tuple[str, ...], default_value: L | None
362357 Returns:
363358 The UpdateSchema with the delete operation staged.
364359 """
365- self ._default_value_updates .append ((path , default_value ))
360+ self ._operations .append (("default_value" , path , default_value ))
366361 self ._set_column_default_value (path , default_value )
367362 return self
368363
@@ -376,7 +371,7 @@ def rename_column(self, path_from: str | tuple[str, ...], new_name: str) -> Upda
376371 Returns:
377372 The UpdateSchema with the rename operation staged.
378373 """
379- self ._column_renames .append ((path_from , new_name ))
374+ self ._operations .append (("rename" , path_from , new_name ))
380375 self ._do_rename_column (path_from , new_name )
381376 return self
382377
@@ -425,12 +420,12 @@ def make_column_optional(self, path: str | tuple[str, ...]) -> UpdateSchema:
425420 Returns:
426421 The UpdateSchema with the requirement change staged.
427422 """
428- self ._optional_columns .append (path )
423+ self ._operations .append (( "optional" , path ) )
429424 self ._set_column_requirement (path , required = False )
430425 return self
431426
432427 def set_identifier_fields (self , * fields : str ) -> None :
433- self ._identifier_field_updates = set ( fields )
428+ self ._operations . append (( "set_identifier_fields" , fields ) )
434429 self ._identifier_field_names = set (fields )
435430
436431 def _set_column_requirement (self , path : str | tuple [str , ...], required : bool ) -> None :
@@ -535,8 +530,7 @@ def update_column(
535530 if field_type is None and required is None and doc is None :
536531 return self
537532
538- # Store intent for retry support
539- self ._column_updates .append ((path , field_type , required , doc ))
533+ self ._operations .append (("update" , path , field_type , required , doc ))
540534 self ._do_update_column (path , field_type , required , doc )
541535 return self
542536
@@ -633,7 +627,7 @@ def move_first(self, path: str | tuple[str, ...]) -> UpdateSchema:
633627 Returns:
634628 The UpdateSchema with the move operation staged.
635629 """
636- self ._move_operations .append (("first " , path , None ))
630+ self ._operations .append (("move_first " , path ))
637631 self ._do_move_first (path )
638632 return self
639633
@@ -657,7 +651,7 @@ def move_before(self, path: str | tuple[str, ...], before_path: str | tuple[str,
657651 Returns:
658652 The UpdateSchema with the move operation staged.
659653 """
660- self ._move_operations .append (("before " , path , before_path ))
654+ self ._operations .append (("move_before " , path , before_path ))
661655 self ._do_move_before (path , before_path )
662656 return self
663657
@@ -695,7 +689,7 @@ def move_after(self, path: str | tuple[str, ...], after_name: str | tuple[str, .
695689 Returns:
696690 The UpdateSchema with the move operation staged.
697691 """
698- self ._move_operations .append (("after " , path , after_name ))
692+ self ._operations .append (("move_after " , path , after_name ))
699693 self ._do_move_after (path , after_name )
700694 return self
701695
0 commit comments