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
30 changes: 30 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 @@ -329,6 +329,36 @@ public static <T> Literal<T> lit(T value) {
return Literals.from(value);
}

/**
* Create a {@link Literal} from a microsecond value.
*
* @param micros a timestamp in microseconds from the unix epoch
* @return a literal representing the timestamp
*/
public static Literal<Long> micros(long micros) {
return new Literals.TimestampLiteral(micros);
}

/**
* Create a {@link Literal} from a millisecond value.
*
* @param millis a timestamp in milliseconds from the unix epoch
* @return a literal representing the timestamp
*/
public static Literal<Long> millis(long millis) {
return new Literals.TimestampLiteral(millis * 1000);
}

/**
* Create a {@link Literal} from a nanosecond value.
*
* @param nanos a timestamp in nanoseconds from the unix epoch
* @return a literal representing the timestamp
*/
public static Literal<Long> nanos(long nanos) {
return new Literals.TimestampNanoLiteral(nanos);
}

public static <T> UnboundAggregate<T> count(String name) {
return new UnboundAggregate<>(Operation.COUNT, ref(name));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ static <T> Literal<T> from(T value) {
Preconditions.checkNotNull(value, "Cannot create expression literal from null");
Preconditions.checkArgument(!NaNUtil.isNaN(value), "Cannot create expression literal from NaN");

if (value instanceof Boolean) {
if (value instanceof Literal) {
return (Literal<T>) value;
} else if (value instanceof Boolean) {
return (Literal<T>) new Literals.BooleanLiteral((Boolean) value);
} else if (value instanceof Integer) {
return (Literal<T>) new Literals.IntegerLiteral((Integer) value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.util.List;
import java.util.concurrent.Callable;
import org.apache.iceberg.Schema;
import org.apache.iceberg.transforms.Transforms;
import org.apache.iceberg.types.Types;
import org.apache.iceberg.types.Types.NestedField;
Expand All @@ -57,6 +59,56 @@
public class TestExpressionHelpers {
private final UnboundPredicate<?> pred = lessThan("x", 7);

@Test
public void testMillisLiteral() {
long now = System.currentTimeMillis();
Literal<Long> millis = Expressions.millis(now);
assertThat(millis.value()).isEqualTo(now * 1000L);
assertThat(millis.to(Types.TimestampNanoType.withZone()).value()).isEqualTo(now * 1_000_000L);
}

@Test
public void testMicrosLiteal() {
long ts = 1510842668000000L;
Literal<Long> micros = Expressions.micros(ts);
assertThat(micros.value()).isEqualTo(ts);
assertThat(micros.to(Types.TimestampNanoType.withZone()).value()).isEqualTo(ts * 1000L);
}

@Test
public void testNanosLiteral() {
long ts = 1510842668000000001L;
Literal<Long> nanos = Expressions.nanos(ts);
assertThat(nanos.value()).isEqualTo(ts);
assertThat(nanos.to(Types.TimestampType.withoutZone()).value()).isEqualTo(1510842668000000L);
}

@Test
public void testMixedTimestampLiterals() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This tests the IN case mentioned in the original thread.

long now = System.currentTimeMillis();
Schema schema = new Schema(NestedField.required(1, "ts", Types.TimestampType.withoutZone()));

// all 3 timestamp values represent the same time and will be deduplicated
Expression expr =
Expressions.in(
"ts",
List.of(
Expressions.millis(now),
Expressions.micros(now * 1000),
Expressions.nanos(now * 1_000_000)));

Expression bound = Binder.bind(schema.asStruct(), expr);

// duplicates are replaced with a single value and in is converted to equals
assertThat(bound).isInstanceOf(BoundPredicate.class);
BoundPredicate<?> predicate = (BoundPredicate<?>) bound;
assertThat(predicate.isLiteralPredicate()).isTrue();
assertThat(predicate.asLiteralPredicate().op()).isEqualTo(Expression.Operation.EQ);
assertThat(predicate.asLiteralPredicate().ref().type())
.isEqualTo(Types.TimestampType.withoutZone());
assertThat(predicate.asLiteralPredicate().literal().value()).isEqualTo(now * 1000);
}

@Test
public void testSimplifyOr() {
assertThat(or(alwaysTrue(), pred))
Expand Down