Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Introduce AssertViewUUID #8831

Merged
merged 2 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions core/src/main/java/org/apache/iceberg/UpdateRequirement.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@

/** Represents a requirement for a {@link MetadataUpdate} */
public interface UpdateRequirement {
void validate(TableMetadata base);
default void validate(TableMetadata base) {
throw new ValidationException(
"Cannot validate %s against a table", this.getClass().getSimpleName());
}

default void validate(ViewMetadata base) {
throw new ValidationException(
Expand Down Expand Up @@ -62,12 +65,25 @@ public void validate(TableMetadata base) {
"Requirement failed: UUID does not match: expected %s != %s", base.uuid(), uuid);
}
}
}

class AssertViewUUID implements UpdateRequirement {
private final String uuid;

public AssertViewUUID(String uuid) {
Preconditions.checkArgument(uuid != null, "Invalid required UUID: null");
this.uuid = uuid;
}

public String uuid() {
return uuid;
}

@Override
public void validate(ViewMetadata base) {
if (!uuid.equalsIgnoreCase(base.uuid())) {
throw new CommitFailedException(
"Requirement failed: UUID does not match: expected %s != %s", base.uuid(), uuid);
"Requirement failed: view UUID does not match: expected %s != %s", base.uuid(), uuid);
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions core/src/main/java/org/apache/iceberg/UpdateRequirementParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ private UpdateRequirementParser() {}

// assertion types
static final String ASSERT_TABLE_UUID = "assert-table-uuid";
static final String ASSERT_VIEW_UUID = "assert-view-uuid";
static final String ASSERT_TABLE_DOES_NOT_EXIST = "assert-create";
static final String ASSERT_REF_SNAPSHOT_ID = "assert-ref-snapshot-id";
static final String ASSERT_LAST_ASSIGNED_FIELD_ID = "assert-last-assigned-field-id";
Expand Down Expand Up @@ -68,6 +69,7 @@ private UpdateRequirementParser() {}
private static final Map<Class<? extends UpdateRequirement>, String> TYPES =
ImmutableMap.<Class<? extends UpdateRequirement>, String>builder()
.put(UpdateRequirement.AssertTableUUID.class, ASSERT_TABLE_UUID)
.put(UpdateRequirement.AssertViewUUID.class, ASSERT_VIEW_UUID)
.put(UpdateRequirement.AssertTableDoesNotExist.class, ASSERT_TABLE_DOES_NOT_EXIST)
.put(UpdateRequirement.AssertRefSnapshotID.class, ASSERT_REF_SNAPSHOT_ID)
.put(UpdateRequirement.AssertLastAssignedFieldId.class, ASSERT_LAST_ASSIGNED_FIELD_ID)
Expand Down Expand Up @@ -101,6 +103,9 @@ public static void toJson(UpdateRequirement updateRequirement, JsonGenerator gen
case ASSERT_TABLE_UUID:
writeAssertTableUUID((UpdateRequirement.AssertTableUUID) updateRequirement, generator);
break;
case ASSERT_VIEW_UUID:
writeAssertViewUUID((UpdateRequirement.AssertViewUUID) updateRequirement, generator);
break;
case ASSERT_REF_SNAPSHOT_ID:
writeAssertRefSnapshotId(
(UpdateRequirement.AssertRefSnapshotID) updateRequirement, generator);
Expand Down Expand Up @@ -159,6 +164,8 @@ public static UpdateRequirement fromJson(JsonNode jsonNode) {
return readAssertTableDoesNotExist(jsonNode);
case ASSERT_TABLE_UUID:
return readAssertTableUUID(jsonNode);
case ASSERT_VIEW_UUID:
return readAssertViewUUID(jsonNode);
case ASSERT_REF_SNAPSHOT_ID:
return readAssertRefSnapshotId(jsonNode);
case ASSERT_LAST_ASSIGNED_FIELD_ID:
Expand All @@ -182,6 +189,11 @@ private static void writeAssertTableUUID(
gen.writeStringField(UUID, requirement.uuid());
}

private static void writeAssertViewUUID(
UpdateRequirement.AssertViewUUID requirement, JsonGenerator gen) throws IOException {
gen.writeStringField(UUID, requirement.uuid());
}

private static void writeAssertRefSnapshotId(
UpdateRequirement.AssertRefSnapshotID requirement, JsonGenerator gen) throws IOException {
gen.writeStringField(NAME, requirement.refName());
Expand Down Expand Up @@ -231,6 +243,11 @@ private static UpdateRequirement readAssertTableUUID(JsonNode node) {
return new UpdateRequirement.AssertTableUUID(uuid);
}

private static UpdateRequirement readAssertViewUUID(JsonNode node) {
String uuid = JsonUtil.getString(UUID, node);
return new UpdateRequirement.AssertViewUUID(uuid);
}

private static UpdateRequirement readAssertRefSnapshotId(JsonNode node) {
String name = JsonUtil.getString(NAME, node);
Long snapshotId = JsonUtil.getLongOrNull(SNAPSHOT_ID, node);
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/org/apache/iceberg/UpdateRequirements.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
import org.apache.iceberg.relocated.com.google.common.collect.Sets;
import org.apache.iceberg.view.ViewMetadata;

public class UpdateRequirements {

Expand Down Expand Up @@ -56,6 +57,16 @@ public static List<UpdateRequirement> forUpdateTable(
return builder.build();
}

public static List<UpdateRequirement> forReplaceView(
ViewMetadata base, List<MetadataUpdate> metadataUpdates) {
Preconditions.checkArgument(null != base, "Invalid view metadata: null");
Preconditions.checkArgument(null != metadataUpdates, "Invalid metadata updates: null");
Builder builder = new Builder(null, false);
builder.require(new UpdateRequirement.AssertViewUUID(base.uuid()));
metadataUpdates.forEach(builder::update);
return builder.build();
}

private static class Builder {
private final TableMetadata base;
private final ImmutableList.Builder<UpdateRequirement> requirements = ImmutableList.builder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import org.apache.iceberg.UpdateRequirement;
import org.apache.iceberg.UpdateRequirements;
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
import org.apache.iceberg.rest.requests.UpdateTableRequest;
import org.apache.iceberg.rest.responses.LoadViewResponse;
import org.apache.iceberg.view.ViewMetadata;
Expand Down Expand Up @@ -62,9 +61,7 @@ public void commit(ViewMetadata base, ViewMetadata metadata) {

UpdateTableRequest request =
UpdateTableRequest.create(
null,
ImmutableList.of(new UpdateRequirement.AssertTableUUID(base.uuid())),
metadata.changes());
null, UpdateRequirements.forReplaceView(base, metadata.changes()), metadata.changes());

LoadViewResponse response =
client.post(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ public void testAssertUUIDToJson() {
.isEqualTo(expected);
}

@Test
public void testAssertViewUUIDFromJson() {
String requirementType = UpdateRequirementParser.ASSERT_VIEW_UUID;
String uuid = "2cc52516-5e73-41f2-b139-545d41a4e151";
String json = String.format("{\"type\":\"assert-view-uuid\",\"uuid\":\"%s\"}", uuid);
UpdateRequirement expected = new UpdateRequirement.AssertViewUUID(uuid);
assertEquals(requirementType, expected, UpdateRequirementParser.fromJson(json));
}

@Test
public void testAssertViewUUIDToJson() {
String uuid = "2cc52516-5e73-41f2-b139-545d41a4e151";
String expected = String.format("{\"type\":\"assert-view-uuid\",\"uuid\":\"%s\"}", uuid);
UpdateRequirement actual = new UpdateRequirement.AssertViewUUID(uuid);
Assertions.assertThat(UpdateRequirementParser.toJson(actual))
.as("AssertViewUUID should convert to the correct JSON value")
.isEqualTo(expected);
}

@Test
public void testAssertTableDoesNotExistFromJson() {
String requirementType = UpdateRequirementParser.ASSERT_TABLE_DOES_NOT_EXIST;
Expand Down Expand Up @@ -262,6 +281,10 @@ public void assertEquals(
(UpdateRequirement.AssertTableUUID) expected,
(UpdateRequirement.AssertTableUUID) actual);
break;
case UpdateRequirementParser.ASSERT_VIEW_UUID:
compareAssertViewUUID(
(UpdateRequirement.AssertViewUUID) expected, (UpdateRequirement.AssertViewUUID) actual);
break;
case UpdateRequirementParser.ASSERT_TABLE_DOES_NOT_EXIST:
// Don't cast here as the function explicitly tests that the types are correct, given that
// the generated JSON
Expand Down Expand Up @@ -312,6 +335,15 @@ private static void compareAssertTableUUID(
.isEqualTo(expected.uuid());
}

private static void compareAssertViewUUID(
UpdateRequirement.AssertViewUUID expected, UpdateRequirement.AssertViewUUID actual) {
Assertions.assertThat(actual.uuid())
.as("UUID from JSON should not be null")
.isNotNull()
.as("UUID should parse correctly from JSON")
.isEqualTo(expected.uuid());
}

// AssertTableDoesNotExist does not have any fields beyond the requirement type, so just check
// that the classes
// are the same and as expected.
Expand Down
Loading