@@ -268,14 +268,15 @@ public TableSchema commitChanges(List<SchemaChange> changes)
268268 tableRoot .toString (), true , branch )));
269269 LazyField <Identifier > lazyIdentifier =
270270 new LazyField <>(() -> identifierFromPath (tableRoot .toString (), true , branch ));
271- TableSchema newTableSchema =
271+ Optional < TableSchema > newTableSchemaOpt =
272272 generateTableSchema (oldTableSchema , changes , hasSnapshots , lazyIdentifier );
273- if (oldTableSchema . sameContent ( newTableSchema )) {
273+ if (! newTableSchemaOpt . isPresent ( )) {
274274 LOG .info (
275275 "No schema change detected for table {}. Skipping schema update." ,
276276 lazyIdentifier .get ());
277277 return oldTableSchema ;
278278 }
279+ TableSchema newTableSchema = newTableSchemaOpt .get ();
279280 try {
280281 boolean success = commit (newTableSchema );
281282 if (success ) {
@@ -287,7 +288,7 @@ public TableSchema commitChanges(List<SchemaChange> changes)
287288 }
288289 }
289290
290- public static TableSchema generateTableSchema (
291+ public static Optional < TableSchema > generateTableSchema (
291292 TableSchema oldTableSchema ,
292293 List <SchemaChange > changes ,
293294 LazyField <Boolean > hasSnapshots ,
@@ -321,7 +322,98 @@ public static TableSchema generateTableSchema(
321322 List <DataField > newFields = new ArrayList <>(oldTableSchema .fields ());
322323 AtomicInteger highestFieldId = new AtomicInteger (oldTableSchema .highestFieldId ());
323324 String newComment = oldTableSchema .comment ();
325+
326+ // Filter out ineffective changes
327+ List <SchemaChange > effectiveChanges = new ArrayList <>();
324328 for (SchemaChange change : changes ) {
329+ if (change instanceof SetOption ) {
330+ SetOption setOption = (SetOption ) change ;
331+ String oldValue = oldOptions .get (setOption .key ());
332+ if (oldValue == null || !oldValue .equals (setOption .value ())) {
333+ effectiveChanges .add (change );
334+ }
335+ } else if (change instanceof RemoveOption ) {
336+ RemoveOption removeOption = (RemoveOption ) change ;
337+ if (oldOptions .containsKey (removeOption .key ())) {
338+ effectiveChanges .add (change );
339+ }
340+ } else if (change instanceof UpdateComment ) {
341+ UpdateComment updateComment = (UpdateComment ) change ;
342+ if (!Objects .equals (oldTableSchema .comment (), updateComment .comment ())) {
343+ effectiveChanges .add (change );
344+ }
345+ } else if (change instanceof RenameColumn ) {
346+ RenameColumn rename = (RenameColumn ) change ;
347+ DataField field = findField (oldTableSchema .fields (), rename .fieldNames ());
348+ if (field != null && !field .name ().equals (rename .newName ())) {
349+ effectiveChanges .add (change );
350+ }
351+ } else if (change instanceof UpdateColumnType ) {
352+ UpdateColumnType update = (UpdateColumnType ) change ;
353+ DataField field = findField (oldTableSchema .fields (), update .fieldNames ());
354+ if (field != null ) {
355+ DataType oldType = field .type ();
356+ DataType newType = update .newDataType ();
357+ if (update .keepNullability ()) {
358+ newType = newType .copy (oldType .isNullable ());
359+ }
360+ if (!oldType .equals (newType )) {
361+ effectiveChanges .add (change );
362+ }
363+ }
364+ } else if (change instanceof UpdateColumnNullability ) {
365+ UpdateColumnNullability update = (UpdateColumnNullability ) change ;
366+ DataField field = findField (oldTableSchema .fields (), update .fieldNames ());
367+ if (field != null ) {
368+ DataType oldType = field .type ();
369+ DataType sourceRootType =
370+ getRootType (
371+ oldType ,
372+ update .fieldNames ().length - 1 ,
373+ update .fieldNames ().length );
374+ if (sourceRootType .isNullable () != update .newNullability ()) {
375+ effectiveChanges .add (change );
376+ }
377+ }
378+ } else if (change instanceof UpdateColumnComment ) {
379+ UpdateColumnComment update = (UpdateColumnComment ) change ;
380+ DataField field = findField (oldTableSchema .fields (), update .fieldNames ());
381+ if (field != null
382+ && !Objects .equals (field .description (), update .newDescription ())) {
383+ effectiveChanges .add (change );
384+ }
385+ } else if (change instanceof UpdateColumnPosition ) {
386+ UpdateColumnPosition update = (UpdateColumnPosition ) change ;
387+ SchemaChange .Move move = update .move ();
388+ String fieldName = move .fieldName ();
389+ DataField field = findFieldByName (newFields , fieldName );
390+ if (field != null ) {
391+ int currentIndex = -1 ;
392+ for (int i = 0 ; i < newFields .size (); i ++) {
393+ if (newFields .get (i ).name ().equals (fieldName )) {
394+ currentIndex = i ;
395+ break ;
396+ }
397+ }
398+ int newIndex = calculateNewPosition (newFields , move );
399+ if (currentIndex != newIndex ) {
400+ effectiveChanges .add (change );
401+ }
402+ }
403+ } else if (change instanceof UpdateColumnDefaultValue ) {
404+ UpdateColumnDefaultValue update = (UpdateColumnDefaultValue ) change ;
405+ DataField field = findField (oldTableSchema .fields (), update .fieldNames ());
406+ if (field != null
407+ && !Objects .equals (field .defaultValue (), update .newDefaultValue ())) {
408+ effectiveChanges .add (change );
409+ }
410+ } else {
411+ // AddColumn and DropColumn always change the schema
412+ effectiveChanges .add (change );
413+ }
414+ }
415+
416+ for (SchemaChange change : effectiveChanges ) {
325417 if (change instanceof SetOption ) {
326418 SetOption setOption = (SetOption ) change ;
327419 if (hasSnapshots .get ()) {
@@ -572,18 +664,75 @@ protected void updateLastColumn(
572664 oldTableSchema .partitionKeys (),
573665 applyNotNestedColumnRename (
574666 oldTableSchema .primaryKeys (),
575- Iterables .filter (changes , RenameColumn .class )),
576- applyRenameColumnsToOptions (newOptions , changes ),
667+ Iterables .filter (effectiveChanges , RenameColumn .class )),
668+ applyRenameColumnsToOptions (newOptions , effectiveChanges ),
577669 newComment );
578670
579- return new TableSchema (
580- oldTableSchema .id () + 1 ,
581- newSchema .fields (),
582- highestFieldId .get (),
583- newSchema .partitionKeys (),
584- newSchema .primaryKeys (),
585- newSchema .options (),
586- newSchema .comment ());
671+ TableSchema newTableSchema =
672+ new TableSchema (
673+ oldTableSchema .id () + 1 ,
674+ newSchema .fields (),
675+ highestFieldId .get (),
676+ newSchema .partitionKeys (),
677+ newSchema .primaryKeys (),
678+ newSchema .options (),
679+ newSchema .comment ());
680+
681+ if (oldTableSchema .sameContent (newTableSchema )) {
682+ return Optional .empty ();
683+ }
684+ return Optional .of (newTableSchema );
685+ }
686+
687+ private static DataField findField (List <DataField > fields , String [] fieldNames ) {
688+ if (fieldNames .length == 0 ) {
689+ return null ;
690+ }
691+ String firstName = fieldNames [0 ];
692+ for (DataField field : fields ) {
693+ if (field .name ().equals (firstName )) {
694+ if (fieldNames .length == 1 ) {
695+ return field ;
696+ }
697+ // Handle nested fields
698+ if (field .type () instanceof RowType ) {
699+ return findField (
700+ ((RowType ) field .type ()).getFields (),
701+ Arrays .copyOfRange (fieldNames , 1 , fieldNames .length ));
702+ }
703+ }
704+ }
705+ return null ;
706+ }
707+
708+ private static DataField findFieldByName (List <DataField > fields , String fieldName ) {
709+ for (DataField field : fields ) {
710+ if (field .name ().equals (fieldName )) {
711+ return field ;
712+ }
713+ }
714+ return null ;
715+ }
716+
717+ private static int calculateNewPosition (List <DataField > fields , SchemaChange .Move move ) {
718+ if (move .type ().equals (SchemaChange .Move .MoveType .FIRST )) {
719+ return 0 ;
720+ } else if (move .type ().equals (SchemaChange .Move .MoveType .LAST )) {
721+ return fields .size () - 1 ;
722+ } else if (move .type ().equals (SchemaChange .Move .MoveType .AFTER )) {
723+ for (int i = 0 ; i < fields .size (); i ++) {
724+ if (fields .get (i ).name ().equals (move .referenceFieldName ())) {
725+ return i + 1 ;
726+ }
727+ }
728+ } else if (move .type ().equals (SchemaChange .Move .MoveType .BEFORE )) {
729+ for (int i = 0 ; i < fields .size (); i ++) {
730+ if (fields .get (i ).name ().equals (move .referenceFieldName ())) {
731+ return i ;
732+ }
733+ }
734+ }
735+ return -1 ;
587736 }
588737
589738 // gets the rootType at the defined depth
0 commit comments