blob: 05945b1cc70f9b294808034dfc336ca29528124d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.druid.query.expression;
import com.google.common.collect.ImmutableList;
import org.apache.druid.common.config.NullHandling;
import org.apache.druid.java.util.common.DateTimes;
import org.apache.druid.java.util.common.IAE;
import org.apache.druid.math.expr.Expr;
import org.apache.druid.math.expr.ExprEval;
import org.apache.druid.math.expr.ExprMacroTable;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Minutes;
import org.joda.time.Months;
import org.joda.time.Years;
import org.junit.Assert;
import org.junit.Test;
import javax.annotation.Nullable;
import java.util.Collections;
public class TimestampShiftMacroTest extends MacroTestBase
{
public TimestampShiftMacroTest()
{
super(new TimestampShiftExprMacro());
}
private DateTime timestamp = DateTimes.of("2020-11-05T04:05:06");
//TIME_SHIFT(<timestamp_expr>, <period>, <step>, [<timezone>])
@Test
public void testZeroArguments()
{
expectException(IAE.class, "Function[timestamp_shift] must have 3 to 4 arguments");
apply(Collections.emptyList());
}
@Test
public void testOneArguments()
{
expectException(IAE.class, "Function[timestamp_shift] must have 3 to 4 arguments");
apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr()
));
}
@Test
public void testTwoArguments()
{
expectException(IAE.class, "Function[timestamp_shift] must have 3 to 4 arguments");
apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr(),
ExprEval.of("P1M").toExpr()
));
}
@Test
public void testMoreThanFourArguments()
{
expectException(IAE.class, "Function[timestamp_shift] must have 3 to 4 arguments");
apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr(),
ExprEval.of("P1M").toExpr(),
ExprEval.of("1").toExpr(),
ExprEval.of("+08:00").toExpr(),
ExprEval.of("extra").toExpr()
));
}
@Test
public void testZeroStep()
{
int step = 0;
Expr expr = apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr(),
ExprEval.of("P1M").toExpr(),
ExprEval.of(step).toExpr()
));
Assert.assertEquals(
timestamp.withPeriodAdded(Months.ONE, step).getMillis(),
expr.eval(ExprUtils.nilBindings()).asLong()
);
}
@Test
public void testPositiveStep()
{
int step = 5;
Expr expr = apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr(),
ExprEval.of("P1M").toExpr(),
ExprEval.of(step).toExpr()
));
Assert.assertEquals(
timestamp.withPeriodAdded(Months.ONE, step).getMillis(),
expr.eval(ExprUtils.nilBindings()).asLong()
);
}
@Test
public void testNegativeStep()
{
int step = -3;
Expr expr = apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr(),
ExprEval.of("P1M").toExpr(),
ExprEval.of(step).toExpr()
));
Assert.assertEquals(
timestamp.withPeriodAdded(Months.ONE, step).getMillis(),
expr.eval(ExprUtils.nilBindings()).asLong()
);
}
@Test
public void testPeriodMinute()
{
Expr expr = apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr(),
ExprEval.of("PT1M").toExpr(),
ExprEval.of(1).toExpr()
));
Assert.assertEquals(
timestamp.withPeriodAdded(Minutes.ONE, 1).getMillis(),
expr.eval(ExprUtils.nilBindings()).asLong()
);
}
@Test
public void testPeriodDay()
{
Expr expr = apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr(),
ExprEval.of("P1D").toExpr(),
ExprEval.of(1).toExpr()
));
Assert.assertEquals(
timestamp.withPeriodAdded(Days.ONE, 1).getMillis(),
expr.eval(ExprUtils.nilBindings()).asLong()
);
}
@Test
public void testPeriodYearAndTimeZone()
{
Expr expr = apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr(),
ExprEval.of("P1Y").toExpr(),
ExprEval.of(1).toExpr(),
ExprEval.of("America/Los_Angeles").toExpr()
));
Assert.assertEquals(
timestamp.toDateTime(DateTimes.inferTzFromString("America/Los_Angeles")).withPeriodAdded(Years.ONE, 1).getMillis(),
expr.eval(ExprUtils.nilBindings()).asLong()
);
}
@Test
public void testDynamicExpression()
{
// step parameter is not a literal expression
Expr expr = apply(
ImmutableList.of(
ExprEval.of(timestamp.getMillis()).toExpr(),
ExprEval.of("P1Y").toExpr(),
new NotLiteralExpr("step"),
ExprEval.of("America/Los_Angeles").toExpr()
));
final int step = 3;
Assert.assertEquals(
timestamp.toDateTime(DateTimes.inferTzFromString("America/Los_Angeles")).withPeriodAdded(Years.ONE, step).getMillis(),
expr.eval(new Expr.ObjectBinding()
{
@Nullable
@Override
public Object get(String name)
{
if ("step".equals(name)) {
return step;
} else {
throw new IAE("Invalid bindings");
}
}
}).asLong()
);
}
@Test
public void testNull()
{
Expr expr = apply(
ImmutableList.of(
ExprEval.ofLong(null).toExpr(),
ExprEval.of("P1M").toExpr(),
ExprEval.of(1L).toExpr()
)
);
if (NullHandling.replaceWithDefault()) {
Assert.assertEquals(2678400000L, expr.eval(ExprUtils.nilBindings()).value());
} else {
Assert.assertNull(expr.eval(ExprUtils.nilBindings()).value());
}
}
private static class NotLiteralExpr extends ExprMacroTable.BaseScalarUnivariateMacroFunctionExpr
{
NotLiteralExpr(String name)
{
super(name, ExprEval.of(name).toExpr());
}
@Override
public ExprEval eval(ObjectBinding bindings)
{
return ExprEval.bestEffortOf(bindings.get(name));
}
@Override
public Expr visit(Shuttle shuttle)
{
return null;
}
}
}