diff --git a/core/src/main/java/org/apache/iceberg/MetadataUpdateParser.java b/core/src/main/java/org/apache/iceberg/MetadataUpdateParser.java index 174d037ee31c..634160954f82 100644 --- a/core/src/main/java/org/apache/iceberg/MetadataUpdateParser.java +++ b/core/src/main/java/org/apache/iceberg/MetadataUpdateParser.java @@ -99,10 +99,16 @@ private MetadataUpdateParser() {} private static final String MAX_REF_AGE_MS = "max-ref-age-ms"; // SetProperties + // the REST API Spec defines "updates" but we initially used "updated", + // thus we need to support reading both indefinitely private static final String UPDATED = "updated"; + private static final String UPDATES = "updates"; // RemoveProperties + // the REST API Spec defines "removals" but we initially used "removed", + // thus we need to support reading both indefinitely private static final String REMOVED = "removed"; + private static final String REMOVALS = "removals"; // SetLocation private static final String LOCATION = "location"; @@ -368,13 +374,13 @@ private static void writeRemoveSnapshotRef( private static void writeSetProperties(MetadataUpdate.SetProperties update, JsonGenerator gen) throws IOException { - gen.writeFieldName(UPDATED); + gen.writeFieldName(UPDATES); gen.writeObject(update.updated()); } private static void writeRemoveProperties( MetadataUpdate.RemoveProperties update, JsonGenerator gen) throws IOException { - gen.writeFieldName(REMOVED); + gen.writeFieldName(REMOVALS); gen.writeObject(update.removed()); } @@ -471,13 +477,35 @@ private static MetadataUpdate readRemoveSnapshotRef(JsonNode node) { } private static MetadataUpdate readSetProperties(JsonNode node) { - Map updated = JsonUtil.getStringMap(UPDATED, node); - return new MetadataUpdate.SetProperties(updated); + Map updates; + + boolean hasLegacyField = node.has(UPDATED); + boolean hasUpdatesField = node.has(UPDATES); + if (hasLegacyField && hasUpdatesField) { + updates = JsonUtil.getStringMap(UPDATES, node); + } else if (hasLegacyField) { + updates = JsonUtil.getStringMap(UPDATED, node); + } else { + updates = JsonUtil.getStringMap(UPDATES, node); + } + + return new MetadataUpdate.SetProperties(updates); } private static MetadataUpdate readRemoveProperties(JsonNode node) { - Set removed = JsonUtil.getStringSet(REMOVED, node); - return new MetadataUpdate.RemoveProperties(removed); + Set removals; + + boolean hasLegacyField = node.has(REMOVED); + boolean hasRemovalsField = node.has(REMOVALS); + if (hasLegacyField && hasRemovalsField) { + removals = JsonUtil.getStringSet(REMOVALS, node); + } else if (hasLegacyField) { + removals = JsonUtil.getStringSet(REMOVED, node); + } else { + removals = JsonUtil.getStringSet(REMOVALS, node); + } + + return new MetadataUpdate.RemoveProperties(removals); } private static MetadataUpdate readSetLocation(JsonNode node) { diff --git a/core/src/test/java/org/apache/iceberg/TestMetadataUpdateParser.java b/core/src/test/java/org/apache/iceberg/TestMetadataUpdateParser.java index 419b172b530f..80faccc5f2d6 100644 --- a/core/src/test/java/org/apache/iceberg/TestMetadataUpdateParser.java +++ b/core/src/test/java/org/apache/iceberg/TestMetadataUpdateParser.java @@ -664,9 +664,23 @@ public void testSetPropertiesFromJson() { "prop1", "val1", "prop2", "val2"); String propsMap = "{\"prop1\":\"val1\",\"prop2\":\"val2\"}"; + + // make sure reading "updated" & "updates" both work String json = String.format("{\"action\":\"%s\",\"updated\":%s}", action, propsMap); MetadataUpdate expected = new MetadataUpdate.SetProperties(props); assertEquals(action, expected, MetadataUpdateParser.fromJson(json)); + + json = String.format("{\"action\":\"%s\",\"updates\":%s}", action, propsMap); + expected = new MetadataUpdate.SetProperties(props); + assertEquals(action, expected, MetadataUpdateParser.fromJson(json)); + + // if "updated" & "updates" are defined, then "updates" takes precedence + json = + String.format( + "{\"action\":\"%s\",\"updates\":%s,\"updated\":{\"propX\":\"valX\"}}", + action, propsMap); + expected = new MetadataUpdate.SetProperties(props); + assertEquals(action, expected, MetadataUpdateParser.fromJson(json)); } @Test @@ -692,7 +706,7 @@ public void testSetPropertiesToJson() { "prop1", "val1", "prop2", "val2"); String propsMap = "{\"prop1\":\"val1\",\"prop2\":\"val2\"}"; - String expected = String.format("{\"action\":\"%s\",\"updated\":%s}", action, propsMap); + String expected = String.format("{\"action\":\"%s\",\"updates\":%s}", action, propsMap); MetadataUpdate update = new MetadataUpdate.SetProperties(props); String actual = MetadataUpdateParser.toJson(update); Assert.assertEquals( @@ -705,9 +719,22 @@ public void testRemovePropertiesFromJson() { String action = MetadataUpdateParser.REMOVE_PROPERTIES; Set toRemove = ImmutableSet.of("prop1", "prop2"); String toRemoveAsJSON = "[\"prop1\",\"prop2\"]"; + + // make sure reading "removed" & "removals" both work String json = String.format("{\"action\":\"%s\",\"removed\":%s}", action, toRemoveAsJSON); MetadataUpdate expected = new MetadataUpdate.RemoveProperties(toRemove); assertEquals(action, expected, MetadataUpdateParser.fromJson(json)); + + json = String.format("{\"action\":\"%s\",\"removals\":%s}", action, toRemoveAsJSON); + expected = new MetadataUpdate.RemoveProperties(toRemove); + assertEquals(action, expected, MetadataUpdateParser.fromJson(json)); + + // if "removed" & "removals" are defined, then "removals" takes precedence + json = + String.format( + "{\"action\":\"%s\",\"removals\":%s,\"removed\": [\"propX\"]}", action, toRemoveAsJSON); + expected = new MetadataUpdate.RemoveProperties(toRemove); + assertEquals(action, expected, MetadataUpdateParser.fromJson(json)); } @Test @@ -715,7 +742,7 @@ public void testRemovePropertiesToJson() { String action = MetadataUpdateParser.REMOVE_PROPERTIES; Set toRemove = ImmutableSet.of("prop1", "prop2"); String toRemoveAsJSON = "[\"prop1\",\"prop2\"]"; - String expected = String.format("{\"action\":\"%s\",\"removed\":%s}", action, toRemoveAsJSON); + String expected = String.format("{\"action\":\"%s\",\"removals\":%s}", action, toRemoveAsJSON); MetadataUpdate update = new MetadataUpdate.RemoveProperties(toRemove); String actual = MetadataUpdateParser.toJson(update); Assert.assertEquals(