mirror of https://github.com/apache/druid.git
Put sleep in an extension (#11632)
* Put sleep in an extension * dependency
This commit is contained in:
parent
1d5438ae7c
commit
2a658acad4
|
@ -142,7 +142,7 @@ public interface Expr extends Cacheable
|
|||
* the entire expression. Otherwise, all vectorizable expressions must produce an output type to correctly operate
|
||||
* with the vectorized engine.
|
||||
*
|
||||
* Outside of the context of vectorized expressions, a return value of null can also indicate that the given type
|
||||
* Outside the context of vectorized expressions, a return value of null can also indicate that the given type
|
||||
* information was not enough to resolve the output type, so the expression must be evaluated using default
|
||||
* {@link #eval} handling where types are only known after evaluation, through {@link ExprEval#type}, such as
|
||||
* transform expressions at ingestion time
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
package org.apache.druid.math.expr;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.apache.druid.common.config.NullHandling;
|
||||
import org.apache.druid.java.util.common.DateTimes;
|
||||
|
@ -28,8 +27,6 @@ import org.apache.druid.java.util.common.IAE;
|
|||
import org.apache.druid.java.util.common.RE;
|
||||
import org.apache.druid.java.util.common.StringUtils;
|
||||
import org.apache.druid.java.util.common.UOE;
|
||||
import org.apache.druid.math.expr.Expr.InputBindingInspector;
|
||||
import org.apache.druid.math.expr.Expr.ObjectBinding;
|
||||
import org.apache.druid.math.expr.vector.CastToTypeVectorProcessor;
|
||||
import org.apache.druid.math.expr.vector.ExprVectorProcessor;
|
||||
import org.apache.druid.math.expr.vector.VectorMathProcessors;
|
||||
|
@ -3642,56 +3639,4 @@ public interface Function
|
|||
return HumanReadableBytes.UnitSystem.DECIMAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function makes the current thread sleep for the given amount of seconds.
|
||||
* Fractional-second delays can be specified.
|
||||
*
|
||||
* This function is applied per row. The actual query time can vary depending on how much parallelism is used
|
||||
* for the query. As it does not provide consistent sleep time, this function should be used only for testing
|
||||
* when you want to keep a certain query running during the test.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
class Sleep implements Function
|
||||
{
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return "sleep";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprEval apply(List<Expr> args, ObjectBinding bindings)
|
||||
{
|
||||
ExprEval eval = args.get(0).eval(bindings);
|
||||
try {
|
||||
if (!eval.isNumericNull()) {
|
||||
double seconds = eval.asDouble();
|
||||
if (seconds > 0) {
|
||||
Thread.sleep((long) (seconds * 1000));
|
||||
}
|
||||
}
|
||||
return ExprEval.of(null);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateArguments(List<Expr> args)
|
||||
{
|
||||
if (args.size() != 1) {
|
||||
throw new IAE("Function[%s] needs 1 argument", name());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ExprType getOutputType(InputBindingInspector inspector, List<Expr> args)
|
||||
{
|
||||
return ExprType.STRING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -783,37 +783,6 @@ public class FunctionTest extends InitializedNullHandlingTest
|
|||
assertExpr("repeat(nonexistent, 10)", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSleep()
|
||||
{
|
||||
assertExpr("sleep(1)", null);
|
||||
assertExpr("sleep(0.5)", null);
|
||||
assertExpr("sleep(null)", null);
|
||||
assertExpr("sleep(0)", null);
|
||||
assertExpr("sleep(-1)", null);
|
||||
|
||||
assertTimeElapsed("sleep(1)", 1000);
|
||||
assertTimeElapsed("sleep(0.5)", 500);
|
||||
assertTimeElapsed("sleep(null)", 0);
|
||||
assertTimeElapsed("sleep(0)", 0);
|
||||
assertTimeElapsed("sleep(-1)", 0);
|
||||
}
|
||||
|
||||
private void assertTimeElapsed(String expression, long expectedTimeElapsedMs)
|
||||
{
|
||||
final long detla = 50;
|
||||
final long before = System.currentTimeMillis();
|
||||
final Expr expr = Parser.parse(expression, ExprMacroTable.nil());
|
||||
expr.eval(bindings).value();
|
||||
final long after = System.currentTimeMillis();
|
||||
final long elapsed = after - before;
|
||||
Assert.assertTrue(
|
||||
StringUtils.format("Expected [%s], but actual elapsed was [%s]", expectedTimeElapsedMs, elapsed),
|
||||
elapsed >= expectedTimeElapsedMs
|
||||
&& elapsed < expectedTimeElapsedMs + detla
|
||||
);
|
||||
}
|
||||
|
||||
private void assertExpr(final String expression, @Nullable final Object expectedResult)
|
||||
{
|
||||
final Expr expr = Parser.parse(expression, ExprMacroTable.nil());
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.apache.druid.extensions</groupId>
|
||||
<artifactId>druid-testing-tools</artifactId>
|
||||
<name>druid-testing-tools</name>
|
||||
<description>Tools useful for testing</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.druid</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>0.22.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.calcite</groupId>
|
||||
<artifactId>calcite-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.druid</groupId>
|
||||
<artifactId>druid-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.druid</groupId>
|
||||
<artifactId>druid-server</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.druid</groupId>
|
||||
<artifactId>druid-processing</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.druid</groupId>
|
||||
<artifactId>druid-sql</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-guava</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-joda</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-smile</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||
<artifactId>jackson-jaxrs-json-provider</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.jaxrs</groupId>
|
||||
<artifactId>jackson-jaxrs-smile-provider</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Test Dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easymock</groupId>
|
||||
<artifactId>easymock</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nl.jqno.equalsverifier</groupId>
|
||||
<artifactId>equalsverifier</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.druid</groupId>
|
||||
<artifactId>druid-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.druid</groupId>
|
||||
<artifactId>druid-processing</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.druid</groupId>
|
||||
<artifactId>druid-server</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.druid</groupId>
|
||||
<artifactId>druid-sql</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.guice;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.google.inject.Binder;
|
||||
import org.apache.druid.initialization.DruidModule;
|
||||
import org.apache.druid.query.expressions.SleepExprMacro;
|
||||
import org.apache.druid.query.sql.SleepOperatorConversion;
|
||||
import org.apache.druid.sql.guice.SqlBindings;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SleepModule implements DruidModule
|
||||
{
|
||||
@Override
|
||||
public List<? extends Module> getJacksonModules()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(Binder binder)
|
||||
{
|
||||
SqlBindings.addOperatorConversion(binder, SleepOperatorConversion.class);
|
||||
ExpressionModule.addExprMacro(binder, SleepExprMacro.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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.expressions;
|
||||
|
||||
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.BaseScalarUnivariateMacroFunctionExpr;
|
||||
import org.apache.druid.math.expr.ExprMacroTable.ExprMacro;
|
||||
import org.apache.druid.math.expr.ExprType;
|
||||
import org.apache.druid.query.expression.ExprUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This function makes the current thread sleep for the given amount of seconds.
|
||||
* Fractional-second delays can be specified.
|
||||
*
|
||||
* This function is applied per row. The actual query time can vary depending on how much parallelism is used
|
||||
* for the query. As it does not provide consistent sleep time, this function should be used only for testing
|
||||
* when you want to keep a certain query running during the test.
|
||||
*/
|
||||
public class SleepExprMacro implements ExprMacro
|
||||
{
|
||||
private static final String NAME = "sleep";
|
||||
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expr apply(List<Expr> args)
|
||||
{
|
||||
if (args.size() != 1) {
|
||||
throw new IAE(ExprUtils.createErrMsg(name(), "must have 1 argument"));
|
||||
}
|
||||
|
||||
Expr arg = args.get(0);
|
||||
|
||||
class SleepExpr extends BaseScalarUnivariateMacroFunctionExpr
|
||||
{
|
||||
public SleepExpr(Expr arg)
|
||||
{
|
||||
super(NAME, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprEval eval(ObjectBinding bindings)
|
||||
{
|
||||
ExprEval eval = arg.eval(bindings);
|
||||
try {
|
||||
if (!eval.isNumericNull()) {
|
||||
double seconds = eval.asDouble(); // double to support fractional-second.
|
||||
if (seconds > 0) {
|
||||
Thread.sleep((long) (seconds * 1000));
|
||||
}
|
||||
}
|
||||
return ExprEval.of(null);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expr visit(Shuttle shuttle)
|
||||
{
|
||||
Expr newArg = arg.visit(shuttle);
|
||||
return shuttle.visit(new SleepExpr(newArg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly override this method to not vectorize the sleep expression.
|
||||
* If we ever want to vectorize this expression, {@link #getOutputType} should be considered to return something
|
||||
* else than just null.
|
||||
*/
|
||||
@Override
|
||||
public boolean canVectorize(InputBindingInspector inspector)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprType getOutputType(InputBindingInspector inspector)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new SleepExpr(arg);
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.druid.sql.calcite.expression.builtin;
|
||||
package org.apache.druid.query.sql;
|
||||
|
||||
import org.apache.calcite.rex.RexNode;
|
||||
import org.apache.calcite.sql.SqlFunction;
|
||||
|
@ -34,7 +34,7 @@ import org.apache.druid.sql.calcite.planner.PlannerContext;
|
|||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A SQL operator conversion for the {@link org.apache.druid.math.expr.Function.Sleep} expression.
|
||||
* A SQL operator conversion for the {@link org.apache.druid.query.expressions.SleepExprMacro} expression.
|
||||
* The expression is currently evaluated during the query planning when the given argument is a number literal.
|
||||
*/
|
||||
public class SleepOperatorConversion implements SqlOperatorConversion
|
|
@ -0,0 +1,16 @@
|
|||
# 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.
|
||||
|
||||
org.apache.druid.guice.SleepModule
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.expressions;
|
||||
|
||||
import org.apache.druid.java.util.common.StringUtils;
|
||||
import org.apache.druid.math.expr.Expr;
|
||||
import org.apache.druid.math.expr.Expr.ObjectBinding;
|
||||
import org.apache.druid.math.expr.ExprMacroTable;
|
||||
import org.apache.druid.math.expr.Parser;
|
||||
import org.apache.druid.testing.InitializedNullHandlingTest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
|
||||
public class SleepExprTest extends InitializedNullHandlingTest
|
||||
{
|
||||
private final ObjectBinding bindings = new ObjectBinding()
|
||||
{
|
||||
@Nullable
|
||||
@Override
|
||||
public Object get(String name)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private final ExprMacroTable exprMacroTable = new ExprMacroTable(Collections.singletonList(new SleepExprMacro()));
|
||||
|
||||
@Test
|
||||
public void testSleep()
|
||||
{
|
||||
assertExpr("sleep(1)");
|
||||
assertExpr("sleep(0.5)");
|
||||
assertExpr("sleep(null)");
|
||||
assertExpr("sleep(0)");
|
||||
assertExpr("sleep(-1)");
|
||||
|
||||
assertTimeElapsed("sleep(1)", 1000);
|
||||
assertTimeElapsed("sleep(0.5)", 500);
|
||||
assertTimeElapsed("sleep(null)", 0);
|
||||
assertTimeElapsed("sleep(0)", 0);
|
||||
assertTimeElapsed("sleep(-1)", 0);
|
||||
}
|
||||
|
||||
private void assertTimeElapsed(String expression, long expectedTimeElapsedMs)
|
||||
{
|
||||
final long detla = 50;
|
||||
final long before = System.currentTimeMillis();
|
||||
final Expr expr = Parser.parse(expression, exprMacroTable);
|
||||
expr.eval(bindings).value();
|
||||
final long after = System.currentTimeMillis();
|
||||
final long elapsed = after - before;
|
||||
Assert.assertTrue(
|
||||
StringUtils.format("Expected [%s], but actual elapsed was [%s]", expectedTimeElapsedMs, elapsed),
|
||||
elapsed >= expectedTimeElapsedMs
|
||||
&& elapsed < expectedTimeElapsedMs + detla
|
||||
);
|
||||
}
|
||||
|
||||
private void assertExpr(final String expression)
|
||||
{
|
||||
final Expr expr = Parser.parse(expression, exprMacroTable);
|
||||
Assert.assertNull(expression, expr.eval(bindings).value());
|
||||
|
||||
final Expr exprNoFlatten = Parser.parse(expression, exprMacroTable, false);
|
||||
final Expr roundTrip = Parser.parse(exprNoFlatten.stringify(), exprMacroTable);
|
||||
Assert.assertNull(expr.stringify(), roundTrip.eval(bindings).value());
|
||||
|
||||
final Expr roundTripFlatten = Parser.parse(expr.stringify(), exprMacroTable);
|
||||
Assert.assertNull(expr.stringify(), roundTripFlatten.eval(bindings).value());
|
||||
|
||||
Assert.assertEquals(expr.stringify(), roundTrip.stringify());
|
||||
Assert.assertEquals(expr.stringify(), roundTripFlatten.stringify());
|
||||
Assert.assertArrayEquals(expr.getCacheKey(), roundTrip.getCacheKey());
|
||||
Assert.assertArrayEquals(expr.getCacheKey(), roundTripFlatten.getCacheKey());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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.sql;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.apache.druid.common.config.NullHandling;
|
||||
import org.apache.druid.guice.ExpressionModule;
|
||||
import org.apache.druid.math.expr.ExprMacroTable;
|
||||
import org.apache.druid.math.expr.ExprMacroTable.ExprMacro;
|
||||
import org.apache.druid.query.Druids;
|
||||
import org.apache.druid.query.TableDataSource;
|
||||
import org.apache.druid.query.expression.LookupExprMacro;
|
||||
import org.apache.druid.query.expressions.SleepExprMacro;
|
||||
import org.apache.druid.query.filter.BoundDimFilter;
|
||||
import org.apache.druid.query.ordering.StringComparators;
|
||||
import org.apache.druid.query.scan.ScanQuery.ResultFormat;
|
||||
import org.apache.druid.segment.column.ValueType;
|
||||
import org.apache.druid.segment.virtual.ExpressionVirtualColumn;
|
||||
import org.apache.druid.sql.calcite.BaseCalciteQueryTest;
|
||||
import org.apache.druid.sql.calcite.filtration.Filtration;
|
||||
import org.apache.druid.sql.calcite.planner.DruidOperatorTable;
|
||||
import org.apache.druid.sql.calcite.util.CalciteTests;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SleepSqlTest extends BaseCalciteQueryTest
|
||||
{
|
||||
@Override
|
||||
public DruidOperatorTable createOperatorTable()
|
||||
{
|
||||
return new DruidOperatorTable(
|
||||
ImmutableSet.of(),
|
||||
ImmutableSet.of(new SleepOperatorConversion())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExprMacroTable createMacroTable()
|
||||
{
|
||||
final List<ExprMacro> exprMacros = new ArrayList<>();
|
||||
for (Class<? extends ExprMacroTable.ExprMacro> clazz : ExpressionModule.EXPR_MACROS) {
|
||||
exprMacros.add(CalciteTests.INJECTOR.getInstance(clazz));
|
||||
}
|
||||
exprMacros.add(CalciteTests.INJECTOR.getInstance(LookupExprMacro.class));
|
||||
exprMacros.add(new SleepExprMacro());
|
||||
return new ExprMacroTable(exprMacros);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSleepFunction() throws Exception
|
||||
{
|
||||
testQuery(
|
||||
"SELECT sleep(m1) from foo where m1 < 2.0",
|
||||
ImmutableList.of(
|
||||
Druids.newScanQueryBuilder()
|
||||
.dataSource(new TableDataSource("foo"))
|
||||
.intervals(querySegmentSpec(Filtration.eternity()))
|
||||
.virtualColumns(
|
||||
new ExpressionVirtualColumn(
|
||||
"v0",
|
||||
"sleep(\"m1\")",
|
||||
ValueType.STRING,
|
||||
createMacroTable()
|
||||
)
|
||||
)
|
||||
.columns("v0")
|
||||
.filters(new BoundDimFilter("m1", null, "2.0", null, true, null, null, StringComparators.NUMERIC))
|
||||
.resultFormat(ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
|
||||
.legacy(false)
|
||||
.context(QUERY_CONTEXT_DEFAULT)
|
||||
.build()
|
||||
),
|
||||
ImmutableList.of(
|
||||
new Object[]{NullHandling.replaceWithDefault() ? "" : null}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
1
pom.xml
1
pom.xml
|
@ -170,6 +170,7 @@
|
|||
<module>extensions-core/druid-basic-security</module>
|
||||
<module>extensions-core/google-extensions</module>
|
||||
<module>extensions-core/druid-ranger-security</module>
|
||||
<module>extensions-core/testing-tools</module>
|
||||
<!-- Community extensions -->
|
||||
<module>extensions-contrib/influx-extensions</module>
|
||||
<module>extensions-contrib/cassandra-storage</module>
|
||||
|
|
|
@ -88,7 +88,7 @@ public class ExprUtils
|
|||
return new PeriodGranularity(period, origin, timeZone);
|
||||
}
|
||||
|
||||
static String createErrMsg(String functionName, String msg)
|
||||
public static String createErrMsg(String functionName, String msg)
|
||||
{
|
||||
String prefix = "Function[" + functionName + "] ";
|
||||
return prefix + msg;
|
||||
|
|
|
@ -97,7 +97,6 @@ import org.apache.druid.sql.calcite.expression.builtin.RepeatOperatorConversion;
|
|||
import org.apache.druid.sql.calcite.expression.builtin.ReverseOperatorConversion;
|
||||
import org.apache.druid.sql.calcite.expression.builtin.RightOperatorConversion;
|
||||
import org.apache.druid.sql.calcite.expression.builtin.RoundOperatorConversion;
|
||||
import org.apache.druid.sql.calcite.expression.builtin.SleepOperatorConversion;
|
||||
import org.apache.druid.sql.calcite.expression.builtin.StringFormatOperatorConversion;
|
||||
import org.apache.druid.sql.calcite.expression.builtin.StringToArrayOperatorConversion;
|
||||
import org.apache.druid.sql.calcite.expression.builtin.StrposOperatorConversion;
|
||||
|
@ -169,7 +168,6 @@ public class DruidOperatorTable implements SqlOperatorTable
|
|||
.add(new TimeParseOperatorConversion())
|
||||
.add(new TimeShiftOperatorConversion())
|
||||
.add(new TimestampToMillisOperatorConversion())
|
||||
.add(new SleepOperatorConversion())
|
||||
.build();
|
||||
|
||||
private static final List<SqlOperatorConversion> STRING_OPERATOR_CONVERSIONS =
|
||||
|
|
|
@ -18858,34 +18858,4 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
|
|||
ImmutableList.of()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSleepFunction() throws Exception
|
||||
{
|
||||
testQuery(
|
||||
"SELECT sleep(m1) from foo where m1 < 2.0",
|
||||
ImmutableList.of(
|
||||
Druids.newScanQueryBuilder()
|
||||
.dataSource(new TableDataSource("foo"))
|
||||
.intervals(querySegmentSpec(Filtration.eternity()))
|
||||
.virtualColumns(
|
||||
new ExpressionVirtualColumn(
|
||||
"v0",
|
||||
"sleep(\"m1\")",
|
||||
ValueType.STRING,
|
||||
ExprMacroTable.nil()
|
||||
)
|
||||
)
|
||||
.columns("v0")
|
||||
.filters(new BoundDimFilter("m1", null, "2.0", null, true, null, null, StringComparators.NUMERIC))
|
||||
.resultFormat(ResultFormat.RESULT_FORMAT_COMPACTED_LIST)
|
||||
.legacy(false)
|
||||
.context(QUERY_CONTEXT_DEFAULT)
|
||||
.build()
|
||||
),
|
||||
ImmutableList.of(
|
||||
new Object[]{NullHandling.replaceWithDefault() ? "" : null}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue