Skip to content
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
27 changes: 27 additions & 0 deletions api/src/main/java/org/apache/iceberg/util/CharSequenceSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
import org.apache.iceberg.relocated.com.google.common.collect.Iterables;
import org.apache.iceberg.relocated.com.google.common.collect.Iterators;
import org.apache.iceberg.relocated.com.google.common.collect.Sets;
import org.apache.iceberg.relocated.com.google.common.collect.Streams;

public class CharSequenceSet implements Set<CharSequence>, Serializable {
private static final ThreadLocal<CharSequenceWrapper> wrappers = ThreadLocal.withInitial(
Expand Down Expand Up @@ -152,4 +155,28 @@ public boolean removeAll(Collection<?> objects) {
public void clear() {
wrapperSet.clear();
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (o == null || getClass() != o.getClass()) {
return false;
}

CharSequenceSet that = (CharSequenceSet) o;
return wrapperSet.equals(that.wrapperSet);
}

@Override
public int hashCode() {
return Objects.hash(wrapperSet);
}

@Override
public String toString() {
return Streams.stream(iterator()).collect(Collectors.joining("CharSequenceSet({", ", ", "})"));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@szehon-ho, I addressed your comment.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public Transaction createOrReplaceTransaction() {
private Transaction newReplaceTableTransaction(boolean orCreate) {
TableOperations ops = newTableOps(identifier);
if (!orCreate && ops.current() == null) {
throw new NoSuchTableException("No such table: %s", identifier);
throw new NoSuchTableException("Table does not exist: %s", identifier);
}

TableMetadata metadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.iceberg.encryption.EncryptionManager;
import org.apache.iceberg.exceptions.AlreadyExistsException;
import org.apache.iceberg.exceptions.CommitFailedException;
import org.apache.iceberg.exceptions.NoSuchTableException;
import org.apache.iceberg.io.FileIO;
Expand Down Expand Up @@ -115,7 +116,12 @@ protected void doRefresh() {
public void commit(TableMetadata base, TableMetadata metadata) {
// if the metadata is already out of date, reject it
if (base != current()) {
throw new CommitFailedException("Cannot commit: stale table metadata");
if (base != null) {
throw new CommitFailedException("Cannot commit: stale table metadata");
} else {
// when current is non-null, the table exists. but when base is null, the commit is trying to create the table
throw new AlreadyExistsException("Table already exists: %s", tableName());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exception message reads a little funny to me, but I think in context it will make sense.

For clarification, what's happening here is that we entered some sort of create table transaction, and then at the end of the transaction, the table was created by some other process. Is that correct?

A comment might help make that more clear but I can see how the stack trace will make sense in context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment to make this clear.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didnt look deeply, but wondering why if caller passes base=null and current is not null , we can tell the table already exists? It seems it would be a new assumption to all the calling code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When base is null, that signals that the commit is trying to create the table and it should not exist. When current is not null, that signals that the table already exists. So in order to throw AlreadyExistsException from the create transaction, this needs to catch that case.

Copy link
Member

@szehon-ho szehon-ho Mar 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea makes sense now, thanks. I guess it may be the same concern as @kbendick :) Maybe we should add this to @base argument javadoc in the TableOperations to clarify it, but itd be unrelated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment to make this clear.

}
}
// if the metadata is not changed, return early
if (base == metadata) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void doCommit(TableMetadata base, TableMetadata metadata) {
try {
Map<String, String> table = getTable();

if (!table.isEmpty()) {
if (base != null) {
validateMetadataLocation(table, base);
String oldMetadataLocation = base.metadataFileLocation();
// Start atomic update
Expand Down Expand Up @@ -123,6 +123,10 @@ public void doCommit(TableMetadata base, TableMetadata metadata) {
} catch (SQLWarning e) {
throw new UncheckedSQLException(e, "Database warning");
} catch (SQLException e) {
// SQLite doesn't set SQLState or throw SQLIntegrityConstraintViolationException
if (e.getMessage().contains("constraint failed")) {
throw new AlreadyExistsException("Table already exists: %s", tableIdentifier);
}
throw new UncheckedSQLException(e, "Unknown failure");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Expand Down
Loading