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

API, Core: Add default value APIs and Avro implementation #9502

Merged
merged 11 commits into from
Oct 4, 2024
5 changes: 5 additions & 0 deletions .palantir/revapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,11 @@ acceptedBreaks:
new: "method void org.apache.iceberg.encryption.Ciphers::<init>()"
justification: "Static utility class - should not have public constructor"
"1.4.0":
org.apache.iceberg:iceberg-api:
- code: "java.class.defaultSerializationChanged"
old: "class org.apache.iceberg.types.Types.NestedField"
new: "class org.apache.iceberg.types.Types.NestedField"
justification: "Add default value APIs."
org.apache.iceberg:iceberg-core:
- code: "java.class.defaultSerializationChanged"
old: "class org.apache.iceberg.PartitionData"
Expand Down
12 changes: 12 additions & 0 deletions api/src/main/java/org/apache/iceberg/expressions/Expressions.java
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,18 @@ public static <T> UnboundTerm<T> transform(String name, Transform<?, T> transfor
return new UnboundTransform<>(ref(name), transform);
}

/**
* Create a {@link Literal} from an Object.
*
* @param value a value
* @param <T> Java type of value
* @return a Literal for the given value
* @throws IllegalArgumentException if the value has no literal implementation
*/
public static <T> Literal<T> lit(T value) {
return Literals.from(value);
}

public static <T> UnboundAggregate<T> count(String name) {
return new UnboundAggregate<>(Operation.COUNT, ref(name));
}
Expand Down
124 changes: 113 additions & 11 deletions api/src/main/java/org/apache/iceberg/types/Types.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.iceberg.Schema;
import org.apache.iceberg.expressions.Expressions;
import org.apache.iceberg.relocated.com.google.common.base.Joiner;
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -414,43 +415,132 @@ public int hashCode() {

public static class NestedField implements Serializable {
public static NestedField optional(int id, String name, Type type) {
return new NestedField(true, id, name, type, null);
return new NestedField(true, id, name, type, null, null, null);
}

public static NestedField optional(int id, String name, Type type, String doc) {
return new NestedField(true, id, name, type, doc);
return new NestedField(true, id, name, type, doc, null, null);
}

public static NestedField required(int id, String name, Type type) {
return new NestedField(false, id, name, type, null);
return new NestedField(false, id, name, type, null, null, null);
}

public static NestedField required(int id, String name, Type type, String doc) {
return new NestedField(false, id, name, type, doc);
return new NestedField(false, id, name, type, doc, null, null);
}

public static NestedField of(int id, boolean isOptional, String name, Type type) {
return new NestedField(isOptional, id, name, type, null);
return new NestedField(isOptional, id, name, type, null, null, null);
}

public static NestedField of(int id, boolean isOptional, String name, Type type, String doc) {
return new NestedField(isOptional, id, name, type, doc);
return new NestedField(isOptional, id, name, type, doc, null, null);
}

public static Builder from(NestedField field) {
return new Builder(field);
}

public static Builder required(String name) {
return new Builder(false, name);
}

public static Builder optional(String name) {
return new Builder(true, name);
}

public static class Builder {
private final boolean isOptional;
private final String name;
private Integer id = null;
private Type type = null;
private String doc = null;
private Object initialDefault = null;
private Object writeDefault = null;

private Builder(boolean isFieldOptional, String fieldName) {
isOptional = isFieldOptional;
name = fieldName;
}

private Builder(NestedField toCopy) {
this.isOptional = toCopy.isOptional;
this.name = toCopy.name;
this.id = toCopy.id;
this.type = toCopy.type;
this.doc = toCopy.doc;
this.initialDefault = toCopy.initialDefault;
this.writeDefault = toCopy.writeDefault;
}

public Builder withId(int fieldId) {
id = fieldId;
return this;
}

public Builder ofType(Type fieldType) {
type = fieldType;
return this;
}

public Builder withDoc(String fieldDoc) {
doc = fieldDoc;
return this;
}

public Builder withInitialDefault(Object fieldInitialDefault) {
initialDefault = fieldInitialDefault;
return this;
}

public Builder withWriteDefault(Object fieldWriteDefault) {
writeDefault = fieldWriteDefault;
return this;
}

public NestedField build() {
// the constructor validates the fields
return new NestedField(isOptional, id, name, type, doc, initialDefault, writeDefault);
}
}

private final boolean isOptional;
private final int id;
private final String name;
private final Type type;
private final String doc;

private NestedField(boolean isOptional, int id, String name, Type type, String doc) {
private final Object initialDefault;
private final Object writeDefault;

private NestedField(
boolean isOptional,
int id,
String name,
Type type,
String doc,
Object initialDefault,
Object writeDefault) {
Preconditions.checkNotNull(name, "Name cannot be null");
Preconditions.checkNotNull(type, "Type cannot be null");
this.isOptional = isOptional;
this.id = id;
this.name = name;
this.type = type;
this.doc = doc;
this.initialDefault = castDefault(initialDefault, type);
this.writeDefault = castDefault(writeDefault, type);
}

private static Object castDefault(Object defaultValue, Type type) {
if (type.isNestedType() && defaultValue != null) {
throw new IllegalArgumentException(
String.format("Invalid default value for %s: %s (must be null)", type, defaultValue));
} else if (defaultValue != null) {
return Expressions.lit(defaultValue).to(type).value();
}

return null;
}

public boolean isOptional() {
Expand All @@ -461,7 +551,7 @@ public NestedField asOptional() {
if (isOptional) {
return this;
}
return new NestedField(true, id, name, type, doc);
return new NestedField(true, id, name, type, doc, initialDefault, writeDefault);
}

public boolean isRequired() {
Expand All @@ -472,11 +562,15 @@ public NestedField asRequired() {
if (!isOptional) {
return this;
}
return new NestedField(false, id, name, type, doc);
return new NestedField(false, id, name, type, doc, initialDefault, writeDefault);
}

/**
* @deprecated will be removed in 2.0.0; use {@link Builder#withId(int)} instead
*/
@Deprecated
public NestedField withFieldId(int newId) {
return new NestedField(isOptional, newId, name, type, doc);
return new NestedField(isOptional, newId, name, type, doc, initialDefault, writeDefault);
}

public int fieldId() {
Expand All @@ -495,6 +589,14 @@ public String doc() {
return doc;
}

public Object initialDefault() {
return initialDefault;
}

public Object writeDefault() {
return writeDefault;
}

@Override
public String toString() {
return String.format("%d: %s: %s %s", id, name, isOptional ? "optional" : "required", type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ public class GenericAvroReader<T>
private Schema fileSchema = null;
private ValueReader<T> reader = null;

public static <D> GenericAvroReader<D> create(org.apache.iceberg.Schema schema) {
return new GenericAvroReader<>(schema);
public static <D> GenericAvroReader<D> create(org.apache.iceberg.Schema expectedSchema) {
return new GenericAvroReader<>(expectedSchema);
}

public static <D> GenericAvroReader<D> create(Schema schema) {
return new GenericAvroReader<>(schema);
public static <D> GenericAvroReader<D> create(Schema readSchema) {
return new GenericAvroReader<>(readSchema);
}

GenericAvroReader(org.apache.iceberg.Schema readSchema) {
this.expectedType = readSchema.asStruct();
GenericAvroReader(org.apache.iceberg.Schema expectedSchema) {
this.expectedType = expectedSchema.asStruct();
}

GenericAvroReader(Schema readSchema) {
Expand Down Expand Up @@ -140,6 +140,8 @@ public ValueReader<?> record(Type partner, Schema record, List<ValueReader<?>> f
Types.NestedField field = expected.field(fieldId);
if (constant != null) {
readPlan.add(Pair.of(pos, ValueReaders.constant(constant)));
} else if (field.initialDefault() != null) {
readPlan.add(Pair.of(pos, ValueReaders.constant(field.initialDefault())));
} else if (fieldId == MetadataColumns.IS_DELETED.fieldId()) {
readPlan.add(Pair.of(pos, ValueReaders.constant(false)));
} else if (fieldId == MetadataColumns.ROW_POSITION.fieldId()) {
Expand Down
Loading