aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVolodymyr Vysotskyi <vvovyk@gmail.com>2019-01-22 00:18:19 +0200
committerVolodymyr Vysotskyi <vvovyk@gmail.com>2019-01-25 17:23:46 +0200
commitc230ba55cceb6d48ac9c1ab0701a167d91842a11 (patch)
treeface3767754e9fba81484e8a12098f3b421963be
parent72cba88f058b072040c701a7e1dbbe4fc4eb8d48 (diff)
DRILL-6533: Allow using literal values in functions which expect FieldReader instead of ValueHolder
closes #1617
-rw-r--r--exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterEvaluator.java50
-rw-r--r--exec/java-exec/src/test/java/org/apache/drill/exec/expr/fn/impl/TestTypeFns.java133
-rw-r--r--exec/vector/src/main/codegen/templates/BasicTypeHelper.java30
3 files changed, 187 insertions, 26 deletions
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterEvaluator.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterEvaluator.java
index a0373d9a7..7648ff4f4 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterEvaluator.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterEvaluator.java
@@ -24,6 +24,7 @@ import java.util.Objects;
import javax.annotation.Nullable;
import javax.inject.Inject;
+import org.apache.drill.exec.expr.BasicTypeHelper;
import org.apache.drill.shaded.guava.com.google.common.base.Function;
import org.apache.drill.common.exceptions.DrillRuntimeException;
import org.apache.drill.common.expression.BooleanOperator;
@@ -89,7 +90,17 @@ public class InterpreterEvaluator {
}
- public static ValueHolder evaluateFunction(DrillSimpleFunc interpreter, ValueHolder[] args, String funcName) throws Exception {
+ /**
+ * Assigns specified {@code Object[] args} to the function arguments,
+ * evaluates function and returns its result.
+ *
+ * @param interpreter function to be evaluated
+ * @param args function arguments
+ * @param funcName name of the function
+ * @return result of function call stored in {@link ValueHolder}
+ * @throws Exception if {@code args} types does not match function input arguments types
+ */
+ public static ValueHolder evaluateFunction(DrillSimpleFunc interpreter, Object[] args, String funcName) throws Exception {
Preconditions.checkArgument(interpreter != null, "interpreter could not be null when use interpreted model to evaluate function " + funcName);
// the current input index to assign into the next available parameter, found using the @Param notation
@@ -100,13 +111,13 @@ public class InterpreterEvaluator {
Field[] fields = interpreter.getClass().getDeclaredFields();
for (Field f : fields) {
// if this is annotated as a parameter to the function
- if ( f.getAnnotation(Param.class) != null ) {
+ if (f.getAnnotation(Param.class) != null) {
f.setAccessible(true);
if (currParameterIndex < args.length) {
f.set(interpreter, args[currParameterIndex]);
}
currParameterIndex++;
- } else if ( f.getAnnotation(Output.class) != null ) {
+ } else if (f.getAnnotation(Output.class) != null) {
f.setAccessible(true);
outField = f;
// create an instance of the holder for the output to be stored in
@@ -127,9 +138,8 @@ public class InterpreterEvaluator {
}
interpreter.setup();
interpreter.eval();
- ValueHolder out = (ValueHolder) outField.get(interpreter);
- return out;
+ return (ValueHolder) outField.get(interpreter);
}
private static class InitVisitor extends AbstractExprVisitor<LogicalExpression, VectorAccessible, RuntimeException> {
@@ -307,25 +317,36 @@ public class InterpreterEvaluator {
DrillSimpleFuncHolder holder = (DrillSimpleFuncHolder) holderExpr.getHolder();
- ValueHolder [] args = new ValueHolder [holderExpr.args.size()];
+ // function arguments may have different types:
+ // usually ValueHolder inheritors but sometimes FieldReader ones
+ Object[] args = new Object[holderExpr.args.size()];
for (int i = 0; i < holderExpr.args.size(); i++) {
- args[i] = holderExpr.args.get(i).accept(this, inIndex);
+ ValueHolder valueHolder = holderExpr.args.get(i).accept(this, inIndex);
+ Object resultArg = valueHolder;
+ TypeProtos.MajorType argType = TypeHelper.getValueHolderType(valueHolder);
+ TypeProtos.MajorType holderParamType = holder.getParameters()[i].getType();
// In case function use "NULL_IF_NULL" policy.
if (holder.getNullHandling() == FunctionTemplate.NullHandling.NULL_IF_NULL) {
// Case 1: parameter is non-nullable, argument is nullable.
- if (holder.getParameters()[i].getType().getMode() == TypeProtos.DataMode.REQUIRED && TypeHelper.getValueHolderType(args[i]).getMode() == TypeProtos.DataMode.OPTIONAL) {
+ if (holderParamType.getMode() == TypeProtos.DataMode.REQUIRED
+ && argType.getMode() == TypeProtos.DataMode.OPTIONAL) {
// Case 1.1 : argument is null, return null value holder directly.
- if (TypeHelper.isNull(args[i])) {
+ if (TypeHelper.isNull(valueHolder)) {
return TypeHelper.createValueHolder(holderExpr.getMajorType());
} else {
// Case 1.2: argument is nullable but not null value, deNullify it.
- args[i] = TypeHelper.deNullify(args[i]);
+ resultArg = TypeHelper.deNullify(valueHolder);
}
- } else if (holder.getParameters()[i].getType().getMode() == TypeProtos.DataMode.OPTIONAL && TypeHelper.getValueHolderType(args[i]).getMode() == TypeProtos.DataMode.REQUIRED) {
+ } else if (holderParamType.getMode() == TypeProtos.DataMode.OPTIONAL
+ && argType.getMode() == TypeProtos.DataMode.REQUIRED) {
// Case 2: parameter is nullable, argument is non-nullable. Nullify it.
- args[i] = TypeHelper.nullify(args[i]);
+ resultArg = TypeHelper.nullify(valueHolder);
}
}
+ if (holder.getParameters()[i].isFieldReader()) {
+ resultArg = BasicTypeHelper.getHolderReaderImpl(argType, valueHolder);
+ }
+ args[i] = resultArg;
}
try {
@@ -333,10 +354,11 @@ public class InterpreterEvaluator {
ValueHolder out = evaluateFunction(interpreter, args, holderExpr.getName());
- if (TypeHelper.getValueHolderType(out).getMode() == TypeProtos.DataMode.OPTIONAL &&
+ TypeProtos.MajorType outputType = TypeHelper.getValueHolderType(out);
+ if (outputType.getMode() == TypeProtos.DataMode.OPTIONAL &&
holderExpr.getMajorType().getMode() == TypeProtos.DataMode.REQUIRED) {
return TypeHelper.deNullify(out);
- } else if (TypeHelper.getValueHolderType(out).getMode() == TypeProtos.DataMode.REQUIRED &&
+ } else if (outputType.getMode() == TypeProtos.DataMode.REQUIRED &&
holderExpr.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL) {
return TypeHelper.nullify(out);
} else {
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/expr/fn/impl/TestTypeFns.java b/exec/java-exec/src/test/java/org/apache/drill/exec/expr/fn/impl/TestTypeFns.java
index 02d664f53..46e321648 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/expr/fn/impl/TestTypeFns.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/expr/fn/impl/TestTypeFns.java
@@ -75,19 +75,19 @@ public class TestTypeFns extends ClusterTest {
// typeof() returns types using the internal names.
String sql = "SELECT typeof(CAST(a AS " + castType + ")) FROM (VALUES (1)) AS T(a)";
- String result = client.queryBuilder().sql(sql).singletonString();
+ String result = queryBuilder().sql(sql).singletonString();
assertEquals(resultType, result);
// For typeof(), null values annoyingly report a type of "NULL"
sql = "SELECT typeof(CAST(a AS " + castType + ")) FROM cp.`functions/null.json`";
- result = client.queryBuilder().sql(sql).singletonString();
+ result = queryBuilder().sql(sql).singletonString();
assertEquals("NULL", result);
}
private void doTypeOfTestSpecial(String expr, String value, String resultType) throws RpcException {
String sql = "SELECT typeof(" + expr + ") FROM (VALUES (" + value + ")) AS T(a)";
- String result = client.queryBuilder().sql(sql).singletonString();
+ String result = queryBuilder().sql(sql).singletonString();
assertEquals(resultType, result);
}
@@ -124,19 +124,25 @@ public class TestTypeFns extends ClusterTest {
// sqlTypeOf() returns SQL type names: the names used in CAST.
String sql = "SELECT sqlTypeOf(CAST(a AS " + type + ")) FROM (VALUES (1)) AS T(a)";
- String result = client.queryBuilder().sql(sql).singletonString();
+ String result = queryBuilder().sql(sql).singletonString();
+ assertEquals(type, result);
+
+ // sqlTypeOf() returns SQL type names: the names used in CAST.
+
+ sql = "SELECT sqlTypeOf(CAST(1 AS " + type + "))";
+ result = queryBuilder().sql(sql).singletonString();
assertEquals(type, result);
// Returns same type even value is null.
sql = "SELECT sqlTypeOf(CAST(a AS " + type + ")) FROM cp.`functions/null.json`";
- result = client.queryBuilder().sql(sql).singletonString();
+ result = queryBuilder().sql(sql).singletonString();
assertEquals(type, result);
}
private void doSqlTypeOfTestSpecial(String expr, String value, String resultType) throws RpcException {
String sql = "SELECT sqlTypeof(" + expr + ") FROM (VALUES (" + value + ")) AS T(a)";
- String result = client.queryBuilder().sql(sql).singletonString();
+ String result = queryBuilder().sql(sql).singletonString();
assertEquals(resultType, result);
}
@@ -163,13 +169,17 @@ public class TestTypeFns extends ClusterTest {
// drillTypeOf() returns types using the internal names.
String sql = "SELECT drillTypeOf(CAST(a AS " + castType + ")) FROM (VALUES (1)) AS T(a)";
- String result = client.queryBuilder().sql(sql).singletonString();
+ String result = queryBuilder().sql(sql).singletonString();
+ assertEquals(resultType, result);
+
+ sql = "SELECT drillTypeOf(CAST(1 AS " + castType + "))";
+ result = queryBuilder().sql(sql).singletonString();
assertEquals(resultType, result);
// Returns same type even value is null.
sql = "SELECT drillTypeOf(CAST(a AS " + castType + ")) FROM cp.`functions/null.json`";
- result = client.queryBuilder().sql(sql).singletonString();
+ result = queryBuilder().sql(sql).singletonString();
assertEquals(resultType, result);
}
@@ -179,19 +189,120 @@ public class TestTypeFns extends ClusterTest {
// CSV files with headers use REQUIRED mode
String sql = "SELECT modeOf(`name`) FROM cp.`store/text/data/cars.csvh`";
- String result = client.queryBuilder().sql(sql).singletonString();
+ String result = queryBuilder().sql(sql).singletonString();
assertEquals("NOT NULL", result);
// CSV files without headers use REPEATED mode
sql = "SELECT modeOf(`columns`) FROM cp.`textinput/input2.csv`";
- result = client.queryBuilder().sql(sql).singletonString();
+ result = queryBuilder().sql(sql).singletonString();
assertEquals("ARRAY", result);
// JSON files use OPTIONAL mode
sql = "SELECT modeOf(`name`) FROM cp.`jsoninput/specialchar.json`";
- result = client.queryBuilder().sql(sql).singletonString();
+ result = queryBuilder().sql(sql).singletonString();
assertEquals("NULLABLE", result);
}
+
+ @Test
+ public void testTypeOfLiteral() throws Exception {
+ String sql =
+ "SELECT typeOf(1) c1," +
+ "typeOf('a') c2," +
+ "typeOf(date '2018-01-22') c3," +
+ "typeOf(time '01:00:20.123') c4," +
+ "typeOf(timestamp '2018-01-22 01:00:20.123') c5," +
+ "typeOf(false) c6," +
+ "typeOf(12.3) c7," +
+ "typeOf(1>2) c8," +
+ "typeOf(cast(null as int)) c9";
+
+ testBuilder()
+ .sqlQuery(sql)
+ .unOrdered()
+ .baselineColumns("c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9")
+ .baselineValues("INT", "VARCHAR", "DATE", "TIME", "TIMESTAMP", "BIT", "VARDECIMAL", "BIT", "NULL")
+ .go();
+ }
+
+ @Test
+ public void testSqlTypeOfLiteral() throws Exception {
+ String sql =
+ "SELECT sqlTypeOf(1) c1," +
+ "sqlTypeOf('a') c2," +
+ "sqlTypeOf(date '2018-01-22') c3," +
+ "sqlTypeOf(time '01:00:20.123') c4," +
+ "sqlTypeOf(timestamp '2018-01-22 01:00:20.123') c5," +
+ "sqlTypeOf(false) c6," +
+ "sqlTypeOf(12.3) c7," +
+ "sqlTypeOf(1>2) c8," +
+ "sqlTypeOf(cast(null as int)) c9";
+
+ testBuilder()
+ .sqlQuery(sql)
+ .unOrdered()
+ .baselineColumns("c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9")
+ .baselineValues("INTEGER", "CHARACTER VARYING", "DATE", "TIME",
+ "TIMESTAMP", "BOOLEAN", "DECIMAL(3, 1)", "BOOLEAN", "INTEGER")
+ .go();
+ }
+
+ @Test
+ public void testDrillTypeOfLiteral() throws Exception {
+ String sql =
+ "SELECT drillTypeOf(1) c1," +
+ "drillTypeOf('a') c2," +
+ "drillTypeOf(date '2018-01-22') c3," +
+ "drillTypeOf(time '01:00:20.123') c4," +
+ "drillTypeOf(timestamp '2018-01-22 01:00:20.123') c5," +
+ "drillTypeOf(false) c6," +
+ "drillTypeOf(12.3) c7," +
+ "drillTypeOf(1>2) c8," +
+ "drillTypeOf(cast(null as int)) c9";
+
+ testBuilder()
+ .sqlQuery(sql)
+ .unOrdered()
+ .baselineColumns("c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9")
+ .baselineValues("INT", "VARCHAR", "DATE", "TIME",
+ "TIMESTAMP", "BIT", "VARDECIMAL", "BIT", "INT")
+ .go();
+ }
+
+ @Test
+ public void testModeOfLiteral() throws Exception {
+ String sql =
+ "SELECT modeOf(1) c1," +
+ "modeOf('a') c2," +
+ "modeOf(cast(null as int)) c3," +
+ "modeOf(case when true then null else 'a' end) c4," +
+ "modeOf(case when false then null else 'a' end) c5";
+
+ testBuilder()
+ .sqlQuery(sql)
+ .unOrdered()
+ .baselineColumns("c1", "c2", "c3", "c4", "c5")
+ .baselineValues("NOT NULL", "NOT NULL", "NULLABLE", "NULLABLE", "NULLABLE")
+ .go();
+ }
+
+ @Test
+ public void testCompareTypeLiteral() throws Exception {
+ String sql =
+ "SELECT compareType(1, 2) c1," +
+ "compareType('a', 1) c2," +
+ "compareType(1, 'a') c3," +
+ "compareType(a, '01:00:20.123') c4," +
+ "compareType(3, t.a) c5," +
+ "compareType(t.a, 3) c6\n" +
+ "from (values(1)) t(a)";
+
+ testBuilder()
+ .sqlQuery(sql)
+ .unOrdered()
+ .baselineColumns("c1", "c2", "c3", "c4", "c5", "c6")
+ .baselineValues(0, 1, -1, -1, 0, 0)
+ .go();
+ }
}
diff --git a/exec/vector/src/main/codegen/templates/BasicTypeHelper.java b/exec/vector/src/main/codegen/templates/BasicTypeHelper.java
index 430a41b43..383e195a0 100644
--- a/exec/vector/src/main/codegen/templates/BasicTypeHelper.java
+++ b/exec/vector/src/main/codegen/templates/BasicTypeHelper.java
@@ -207,7 +207,35 @@ public class BasicTypeHelper {
throw new UnsupportedOperationException(buildErrorMessage("get writer implementation", type, mode));
}
- public static Class<?> getHolderReaderImpl( MinorType type, DataMode mode){
+ /**
+ * Creates and returns {@link FieldReader} instance for specified {@code MajorType type} using specisied {@code ValueHolder}
+ *
+ * @param type type of resulting {@link FieldReader} instance
+ * @param holder value holder for {@link FieldReader} creation
+ * @return {@link FieldReader} instance
+ */
+ public static FieldReader getHolderReaderImpl(MajorType type, ValueHolder holder) {
+ switch (type.getMinorType()) {
+ <#list vv.types as type>
+ <#list type.minor as minor>
+ case ${minor.class?upper_case}:
+ switch (type.getMode()) {
+ case REQUIRED:
+ return new ${minor.class}HolderReaderImpl((${minor.class}Holder) holder);
+ case OPTIONAL:
+ return new Nullable${minor.class}HolderReaderImpl((Nullable${minor.class}Holder) holder);
+ case REPEATED:
+ return new Repeated${minor.class}HolderReaderImpl((Repeated${minor.class}Holder) holder);
+ }
+ </#list>
+ </#list>
+ case NULL:
+ return new UntypedHolderReaderImpl((UntypedNullHolder) holder);
+ }
+ throw new UnsupportedOperationException(buildErrorMessage("get holder reader implementation", type.getMinorType(), type.getMode()));
+ }
+
+ public static Class<?> getHolderReaderImpl(MinorType type, DataMode mode) {
switch (type) {
<#list vv.types as type>
<#list type.minor as minor>