DRILL-1383: Support interpreted execution for Drill expression tree. Fix OOM while running InterpreterBuilder
Project: http://git-wip-us.apache.org/repos/asf/incubator-drill/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-drill/commit/9bc71fc5 Tree: http://git-wip-us.apache.org/repos/asf/incubator-drill/tree/9bc71fc5 Diff: http://git-wip-us.apache.org/repos/asf/incubator-drill/diff/9bc71fc5 Branch: refs/heads/master Commit: 9bc71fc54b97b52ac5c7335247b6ca7887045fd2 Parents: 8b8d787 Author: Jinfeng Ni <j...@maprtech.com> Authored: Thu Sep 4 20:40:50 2014 -0700 Committer: Steven Phillips <sphill...@maprtech.com> Committed: Tue Sep 23 22:23:02 2014 -0700 ---------------------------------------------------------------------- exec/interpreter/pom.xml | 109 ++++++ .../exec/expr/ExpressionInterpreterTest.java | 201 +++++++++++ .../src/main/codegen/templates/TypeHelper.java | 217 +++++++++++- .../drill/exec/expr/DrillFuncHolderExpr.java | 10 + .../exec/expr/fn/DrillBooleanOPHolder.java | 2 +- .../drill/exec/expr/fn/DrillFuncHolder.java | 25 ++ .../exec/expr/fn/DrillSimpleFuncHolder.java | 27 +- .../drill/exec/expr/fn/FunctionConverter.java | 5 +- .../fn/interpreter/DrillFuncInterpreter.java | 26 ++ .../interpreter/DrillSimpleFuncInterpreter.java | 30 ++ .../expr/fn/interpreter/InterpreterBuilder.java | 69 ++++ .../fn/interpreter/InterpreterEvaluator.java | 334 +++++++++++++++++++ .../fn/interpreter/InterpreterGenerator.java | 143 ++++++++ .../drill/exec/vector/ValueHolderHelper.java | 52 +++ exec/pom.xml | 1 + 15 files changed, 1234 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/interpreter/pom.xml ---------------------------------------------------------------------- diff --git a/exec/interpreter/pom.xml b/exec/interpreter/pom.xml new file mode 100644 index 0000000..0773a71 --- /dev/null +++ b/exec/interpreter/pom.xml @@ -0,0 +1,109 @@ +<?xml version="1.0"?> +<!-- 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/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.drill.exec</groupId> + <artifactId>exec-parent</artifactId> + <version>0.6.0-incubating-SNAPSHOT</version> + </parent> + <artifactId>drill-interpreter</artifactId> + <name>exec/Drill expression interpreter</name> + + <dependencies> + <dependency> + <groupId>org.apache.drill</groupId> + <artifactId>drill-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.drill.exec</groupId> + <artifactId>drill-java-exec</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.drill</groupId> + <artifactId>drill-common</artifactId> + <classifier>tests</classifier> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.drill.exec</groupId> + <artifactId>drill-java-exec</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.codehaus.janino</groupId> + <artifactId>janino</artifactId> + <version>2.6.1</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <dependencies> + <dependency> + <groupId>org.apache.drill.exec</groupId> + <artifactId>drill-java-exec</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + <executions> + <execution> + <id>generate-sources</id> + <phase>generate-sources</phase> + <goals> + <goal>exec</goal> + </goals> + <configuration> + <executable>java</executable> + <arguments> + <argument>-Xmx1024m</argument> + <argument>-XX:MaxPermSize=256m</argument> + <argument>-classpath</argument> + <classpath/> + <argument>org.apache.drill.exec.expr.fn.interpreter.InterpreterBuilder</argument> + <argument>${project.build.directory}/generated-sources</argument> + </arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>1.9.1</version> + <executions> + <execution> + <id>add-generated-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>${project.build.directory}/generated-sources</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/interpreter/src/test/java/org/apache/drill/exec/expr/ExpressionInterpreterTest.java ---------------------------------------------------------------------- diff --git a/exec/interpreter/src/test/java/org/apache/drill/exec/expr/ExpressionInterpreterTest.java b/exec/interpreter/src/test/java/org/apache/drill/exec/expr/ExpressionInterpreterTest.java new file mode 100644 index 0000000..a94ef94 --- /dev/null +++ b/exec/interpreter/src/test/java/org/apache/drill/exec/expr/ExpressionInterpreterTest.java @@ -0,0 +1,201 @@ +/** + * 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.drill.exec.expr; + + +import com.google.common.collect.Lists; +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.RecognitionException; +import org.apache.drill.common.exceptions.DrillRuntimeException; +import org.apache.drill.common.expression.ErrorCollector; +import org.apache.drill.common.expression.ErrorCollectorImpl; +import org.apache.drill.common.expression.LogicalExpression; +import org.apache.drill.common.expression.parser.ExprLexer; +import org.apache.drill.common.expression.parser.ExprParser; +import org.apache.drill.common.types.TypeProtos; +import org.apache.drill.common.types.Types; +import org.apache.drill.common.util.DrillStringUtils; +import org.apache.drill.exec.expr.fn.interpreter.InterpreterEvaluator; +import org.apache.drill.exec.ops.FragmentContext; +import org.apache.drill.exec.pop.PopUnitTestBase; +import org.apache.drill.exec.proto.BitControl; +import org.apache.drill.exec.record.MaterializedField; +import org.apache.drill.exec.record.RecordBatch; +import org.apache.drill.exec.server.Drillbit; +import org.apache.drill.exec.server.RemoteServiceSet; +import org.apache.drill.exec.store.mock.MockGroupScanPOP; +import org.apache.drill.exec.store.mock.MockScanBatchCreator; +import org.apache.drill.exec.store.mock.MockSubScanPOP; +import org.apache.drill.exec.vector.ValueVector; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class ExpressionInterpreterTest extends PopUnitTestBase { + static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ExpressionInterpreterTest.class); + + @Test + public void interpreterNullableStrExpr() throws Exception { + String[] colNames = {"col1"}; + TypeProtos.MajorType[] colTypes = {Types.optional(TypeProtos.MinorType.VARCHAR)}; + String expressionStr = "substr(col1, 1, 3)"; + String[] expectedFirstTwoValues = {"aaa", "null"}; + + doTest(expressionStr, colNames, colTypes, expectedFirstTwoValues); + } + + + @Test + public void interpreterNullableBooleanExpr() throws Exception { + String[] colNames = {"col1"}; + TypeProtos.MajorType[] colTypes = {Types.optional(TypeProtos.MinorType.VARCHAR)}; + String expressionStr = "col1 < 'abc' and col1 > 'abc'"; + String[] expectedFirstTwoValues = {"false", "null"}; + + doTest(expressionStr, colNames, colTypes, expectedFirstTwoValues); + } + + + @Test + public void interpreterNullableIntegerExpr() throws Exception { + String[] colNames = {"col1"}; + TypeProtos.MajorType[] colTypes = {Types.optional(TypeProtos.MinorType.INT)}; + String expressionStr = "col1 + 100 - 1 * 2 + 2"; + String[] expectedFirstTwoValues = {"-2147483548", "null"}; + + doTest(expressionStr, colNames, colTypes, expectedFirstTwoValues); + } + + @Test + public void interpreterCaseExpr() throws Exception { + String[] colNames = {"col1"}; + TypeProtos.MajorType[] colTypes = {Types.optional(TypeProtos.MinorType.VARCHAR)}; + String expressionStr = "case when substr(col1, 1, 3)='aaa' then 'ABC' else 'XYZ' end"; + String[] expectedFirstTwoValues = {"ABC", "XYZ"}; + + doTest(expressionStr, colNames, colTypes, expectedFirstTwoValues); + } + + + protected void doTest(String expressionStr, String[] colNames, TypeProtos.MajorType[] colTypes, String[] expectFirstTwoValues) throws Exception { + RemoteServiceSet serviceSet = RemoteServiceSet.getLocalServiceSet(); + + Drillbit bit1 = new Drillbit(CONFIG, serviceSet); + + bit1.run(); + + // Create a mock scan batch as input for evaluation. + assert(colNames.length == colTypes.length); + + MockGroupScanPOP.MockColumn[] columns = new MockGroupScanPOP.MockColumn[colNames.length]; + + for (int i = 0; i < colNames.length; i++ ) { + columns[i] = new MockGroupScanPOP.MockColumn(colNames[i], colTypes[i].getMinorType(), colTypes[i].getMode(),0,0,0); + } + + MockGroupScanPOP.MockScanEntry entry = new MockGroupScanPOP.MockScanEntry(10, columns); + MockSubScanPOP scanPOP = new MockSubScanPOP("testTable", java.util.Collections.singletonList(entry)); + + RecordBatch batch = createMockScanBatch(bit1, scanPOP); + + batch.next(); + + ValueVector vv = evalExprWithInterpreter(expressionStr, batch, bit1); + + // Verify the first 2 values in the output of evaluation. + assert(expectFirstTwoValues.length == 2); + assertEquals(expectFirstTwoValues[0], getValueFromVector(vv, 0)); + assertEquals(expectFirstTwoValues[1], getValueFromVector(vv, 1)); + + showValueVectorContent(vv); + + vv.clear(); + batch.cleanup(); + batch.getContext().close(); + bit1.close(); + } + + + private RecordBatch createMockScanBatch(Drillbit bit, MockSubScanPOP scanPOP) { + List<RecordBatch> children = Lists.newArrayList(); + MockScanBatchCreator creator = new MockScanBatchCreator(); + + try { + FragmentContext context = new FragmentContext(bit.getContext(), BitControl.PlanFragment.getDefaultInstance(), null, bit.getContext().getFunctionImplementationRegistry()); + return creator.getBatch(context,scanPOP, children); + } catch (Exception ex) { + throw new DrillRuntimeException("Error when setup fragment context" + ex); + } + } + + private LogicalExpression parseExpr(String expr) throws RecognitionException { + ExprLexer lexer = new ExprLexer(new ANTLRStringStream(expr)); + CommonTokenStream tokens = new CommonTokenStream(lexer); + ExprParser parser = new ExprParser(tokens); + ExprParser.parse_return ret = parser.parse(); + return ret.e; + } + + private ValueVector evalExprWithInterpreter(String expression, RecordBatch batch, Drillbit bit) throws Exception { + LogicalExpression expr = parseExpr(expression); + ErrorCollector error = new ErrorCollectorImpl(); + LogicalExpression materializedExpr = ExpressionTreeMaterializer.materialize(expr, batch, error, bit.getContext().getFunctionImplementationRegistry()); + if (error.getErrorCount() != 0) { + logger.error("Failure while materializing expression [{}]. Errors: {}", expression, error); + assertEquals(0, error.getErrorCount()); + } + + final MaterializedField outputField = MaterializedField.create("outCol", materializedExpr.getMajorType()); + + ValueVector vector = TypeHelper.getNewVector(outputField, bit.getContext().getAllocator()); + + vector.allocateNewSafe(); + + InterpreterEvaluator.evaluate(batch, vector, materializedExpr); + + return vector; + } + + private void showValueVectorContent(ValueVector vw) { + for (int row = 0; row < vw.getAccessor().getValueCount(); row ++ ) { + Object o = vw.getAccessor().getObject(row); + String cellString; + if (o instanceof byte[]) { + cellString = DrillStringUtils.toBinaryString((byte[]) o); + } else { + cellString = DrillStringUtils.escapeNewLines(String.valueOf(o)); + } + System.out.printf(row + "th value: " + cellString + "\n"); + } + } + + private String getValueFromVector(ValueVector vw, int index) { + Object o = vw.getAccessor().getObject(index); + String cellString; + if (o instanceof byte[]) { + cellString = DrillStringUtils.toBinaryString((byte[]) o); + } else { + cellString = DrillStringUtils.escapeNewLines(String.valueOf(o)); + } + return cellString; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/codegen/templates/TypeHelper.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/codegen/templates/TypeHelper.java b/exec/java-exec/src/main/codegen/templates/TypeHelper.java index 9bb2507..3c0d9d3 100644 --- a/exec/java-exec/src/main/codegen/templates/TypeHelper.java +++ b/exec/java-exec/src/main/codegen/templates/TypeHelper.java @@ -16,6 +16,8 @@ * limitations under the License. */ +import org.apache.drill.common.types.MajorType; + <@pp.dropOutputFile /> <@pp.changeOutputFile name="/org/apache/drill/exec/expr/TypeHelper.java" /> @@ -305,11 +307,33 @@ public class TypeHelper { case ${minor.class?upper_case} : <#if minor.class?starts_with("Var") || minor.class == "TimeStampTZ" || minor.class == "IntervalDay" || minor.class == "Interval" || minor.class?starts_with("Decimal28") || minor.class?starts_with("Decimal38")> - throw new UnsupportedOperationException(type.getMinorType() + " type is not supported."); + switch (type.getMode()) { + case REQUIRED: + holder = new ${minor.class}Holder(); + ((${minor.class}Vector) vector).getAccessor().get(index, (${minor.class}Holder)holder); + return holder; + case OPTIONAL: + holder = new Nullable${minor.class}Holder(); + ((Nullable${minor.class}Holder)holder).isSet = ((Nullable${minor.class}Vector) vector).getAccessor().isSet(index); + if (((Nullable${minor.class}Holder)holder).isSet == 1) { + ((Nullable${minor.class}Vector) vector).getAccessor().get(index, (Nullable${minor.class}Holder)holder); + } + return holder; + } <#else> - holder = new ${minor.class}Holder(); - ((${minor.class}Holder)holder).value = ((${minor.class}Vector) vector).getAccessor().get(index); - break; + switch (type.getMode()) { + case REQUIRED: + holder = new ${minor.class}Holder(); + ((${minor.class}Holder)holder).value = ((${minor.class}Vector) vector).getAccessor().get(index); + return holder; + case OPTIONAL: + holder = new Nullable${minor.class}Holder(); + ((Nullable${minor.class}Holder)holder).isSet = ((Nullable${minor.class}Vector) vector).getAccessor().isSet(index); + if (((Nullable${minor.class}Holder)holder).isSet == 1) { + ((Nullable${minor.class}Holder)holder).value = ((Nullable${minor.class}Vector) vector).getAccessor().get(index); + } + return holder; + } </#if> </#list> </#list> @@ -318,9 +342,10 @@ public class TypeHelper { ((ObjectHolder)holder).obj = ((ObjectVector) vector).getAccessor().getObject(index) ; break; default: - throw new UnsupportedOperationException(type.getMinorType() + " type is not supported."); + throw new UnsupportedOperationException(type + " type is not supported."); } - return holder; + + throw new UnsupportedOperationException(type + " type is not supported."); } public static void setValue(ValueVector vector, int index, ValueHolder holder) { @@ -330,15 +355,54 @@ public class TypeHelper { <#list vv.types as type> <#list type.minor as minor> case ${minor.class?upper_case} : - ((${minor.class}Vector) vector).getMutator().setSafe(index, (${minor.class}Holder) holder); - break; + switch (type.getMode()) { + case REQUIRED: + ((${minor.class}Vector) vector).getMutator().setSafe(index, (${minor.class}Holder) holder); + return; + case OPTIONAL: + if (((Nullable${minor.class}Holder) holder).isSet == 1) { + ((Nullable${minor.class}Vector) vector).getMutator().setSafe(index, (Nullable${minor.class}Holder) holder); + } + return; + } </#list> </#list> case GENERIC_OBJECT: ((ObjectVector) vector).getMutator().setSafe(index, (ObjectHolder) holder); - break ; + return; default: - throw new UnsupportedOperationException(type.getMinorType() + " type is not supported."); + throw new UnsupportedOperationException(type.getMinorType() + " type is not supported."); + } + } + + public static boolean setValueSafe(ValueVector vector, int index, ValueHolder holder) { + MajorType type = vector.getField().getType(); + + switch(type.getMinorType()) { + <#list vv.types as type> + <#list type.minor as minor> + case ${minor.class?upper_case} : + switch (type.getMode()) { + case REQUIRED: + return ((${minor.class}Vector) vector).getMutator().setSafe(index, (${minor.class}Holder) holder); + case OPTIONAL: + if (((Nullable${minor.class}Holder) holder).isSet == 1) { + if (! ((Nullable${minor.class}Vector) vector).getMutator().setSafe(index, (Nullable${minor.class}Holder) holder) ) { + return false; + } + } else { + if (!((Nullable${minor.class}Vector) vector).getMutator().isSafe(index)) { + return false; + } + } + return true; + } + </#list> + </#list> + case GENERIC_OBJECT: + return ((ObjectVector) vector).getMutator().setSafe(index, (ObjectHolder) holder); + default: + throw new UnsupportedOperationException(type.getMinorType() + " type is not supported."); } } @@ -366,4 +430,137 @@ public class TypeHelper { return false; } + /** + * Create a ValueHolder of MajorType. + * @param type + * @return + */ + public static ValueHolder createValueHolder(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}Holder(); + case OPTIONAL: + return new Nullable${minor.class}Holder(); + case REPEATED: + return new Repeated${minor.class}Holder(); + } + </#list> + </#list> + case GENERIC_OBJECT: + return new ObjectHolder(); + default: + throw new UnsupportedOperationException(type + " type is not supported for 'createValueHolder' method."); + } + } + + public static boolean isNull(ValueHolder holder) { + MajorType type = TypeHelper.getValueHolderType(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 true; + case OPTIONAL: + return ((Nullable${minor.class}Holder) holder).isSet == 0; + case REPEATED: + return true; + } + </#list> + </#list> + default: + throw new UnsupportedOperationException(type + " type is not supported for 'isNull' method."); + } + } + + public static ValueHolder deNullify(ValueHolder holder) { + MajorType type = TypeHelper.getValueHolderType(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 holder; + case OPTIONAL: + if( ((Nullable${minor.class}Holder) holder).isSet == 1) { + ${minor.class}Holder newHolder = new ${minor.class}Holder(); + + <#assign fields = minor.fields!type.fields /> + <#list fields as field> + newHolder.${field.name} = ((Nullable${minor.class}Holder) holder).${field.name}; + </#list> + + return newHolder; + } else { + throw new UnsupportedOperationException("You can not convert a null value into a non-null value!"); + } + case REPEATED: + return holder; + } + </#list> + </#list> + default: + throw new UnsupportedOperationException(type + " type is not supported for 'deNullify' method."); + } + } + + public static ValueHolder nullify(ValueHolder holder) { + MajorType type = TypeHelper.getValueHolderType(holder); + + switch(type.getMinorType()) { + <#list vv.types as type> + <#list type.minor as minor> + case ${minor.class?upper_case} : + switch (type.getMode()) { + case REQUIRED: + Nullable${minor.class}Holder newHolder = new Nullable${minor.class}Holder(); + newHolder.isSet = 1; + <#assign fields = minor.fields!type.fields /> + <#list fields as field> + newHolder.${field.name} = ((${minor.class}Holder) holder).${field.name}; + </#list> + return newHolder; + case OPTIONAL: + return holder; + case REPEATED: + throw new UnsupportedOperationException("You can not convert repeated type " + type + " to nullable type!"); + } + </#list> + </#list> + default: + throw new UnsupportedOperationException(type + " type is not supported for 'nullify' method."); + } + } + + public static MajorType getValueHolderType(ValueHolder holder) { + + if (0 == 1) { + return null; + } + <#list vv.types as type> + <#list type.minor as minor> + else if (holder instanceof ${minor.class}Holder) { + return ((${minor.class}Holder) holder).TYPE; + } else if (holder instanceof Nullable${minor.class}Holder) { + return ((Nullable${minor.class}Holder) holder).TYPE; + } + </#list> + </#list> + + throw new UnsupportedOperationException("ValueHolder is not supported for 'getValueHolderType' method."); + + } + } http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/DrillFuncHolderExpr.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/DrillFuncHolderExpr.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/DrillFuncHolderExpr.java index d1734d4..bc631b8 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/DrillFuncHolderExpr.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/DrillFuncHolderExpr.java @@ -27,10 +27,12 @@ import org.apache.drill.common.expression.fn.FuncHolder; import org.apache.drill.common.types.TypeProtos.MajorType; import org.apache.drill.exec.expr.fn.DrillComplexWriterFuncHolder; import org.apache.drill.exec.expr.fn.DrillFuncHolder; +import org.apache.drill.exec.expr.fn.interpreter.DrillSimpleFuncInterpreter; public class DrillFuncHolderExpr extends FunctionHolderExpression implements Iterable<LogicalExpression>{ static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillFuncHolderExpr.class); private DrillFuncHolder holder; + private DrillSimpleFuncInterpreter interpreter; public DrillFuncHolderExpr(String nameUsed, DrillFuncHolder holder, List<LogicalExpression> args, ExpressionPosition pos) { super(nameUsed, pos, args); @@ -92,5 +94,13 @@ public class DrillFuncHolderExpr extends FunctionHolderExpression implements Ite return new DrillFuncHolderExpr(this.nameUsed, this.holder, args, this.getPosition()); } + public void setInterpreter(DrillSimpleFuncInterpreter interpreter) { + this.interpreter = interpreter; + } + + public DrillSimpleFuncInterpreter getInterpreter() { + return this.interpreter; + } + } http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillBooleanOPHolder.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillBooleanOPHolder.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillBooleanOPHolder.java index 9032d37..743598a 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillBooleanOPHolder.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillBooleanOPHolder.java @@ -30,7 +30,7 @@ public class DrillBooleanOPHolder extends DrillSimpleFuncHolder{ public DrillBooleanOPHolder(FunctionScope scope, NullHandling nullHandling, boolean isBinaryCommutative, boolean isRandom, String[] registeredNames, ValueReference[] parameters, ValueReference returnValue, WorkspaceReference[] workspaceVars, Map<String, String> methods, List<String> imports) { - super(scope, nullHandling, isBinaryCommutative, isRandom, registeredNames, parameters, returnValue, workspaceVars, methods, imports, FunctionCostCategory.getDefault()); + super(scope, nullHandling, isBinaryCommutative, isRandom, registeredNames, parameters, returnValue, workspaceVars, methods, imports); } } http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java index 1852731..279c428 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillFuncHolder.java @@ -277,6 +277,14 @@ public abstract class DrillFuncHolder extends AbstractFuncHolder { + ", parameters=" + (parameters != null ? Arrays.asList(parameters).subList(0, Math.min(parameters.length, maxLen)) : null) + "]"; } + public WorkspaceReference[] getWorkspaceVars() { + return this.workspaceVars; + } + + public ValueReference[] getParameters() { + return this.parameters; + } + public static class ValueReference { MajorType type; String name; @@ -292,6 +300,14 @@ public abstract class DrillFuncHolder extends AbstractFuncHolder { this.name = name; } + public MajorType getType() { + return type; + } + + public String getName() { + return name; + } + public void setConstant(boolean isConstant) { this.isConstant = isConstant; } @@ -344,6 +360,15 @@ public abstract class DrillFuncHolder extends AbstractFuncHolder { public boolean isInject() { return inject; } + + public Class<?> getType() { + return type; + } + + public String getName() { + return name; + } + } public boolean checkPrecisionRange() { http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillSimpleFuncHolder.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillSimpleFuncHolder.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillSimpleFuncHolder.java index 4731200..ec284a7 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillSimpleFuncHolder.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/DrillSimpleFuncHolder.java @@ -37,24 +37,27 @@ import com.sun.codemodel.JExpr; import com.sun.codemodel.JExpression; import com.sun.codemodel.JMod; import com.sun.codemodel.JVar; +import org.apache.drill.exec.expr.fn.interpreter.DrillSimpleFuncInterpreter; +import org.apache.drill.exec.expr.fn.interpreter.InterpreterGenerator; -class DrillSimpleFuncHolder extends DrillFuncHolder{ +public class DrillSimpleFuncHolder extends DrillFuncHolder{ static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillSimpleFuncHolder.class); private final String setupBody; private final String evalBody; private final String resetBody; private final String cleanupBody; + private final String interpreterClassName; public DrillSimpleFuncHolder(FunctionScope scope, NullHandling nullHandling, boolean isBinaryCommutative, boolean isRandom, String[] registeredNames, ValueReference[] parameters, ValueReference returnValue, WorkspaceReference[] workspaceVars, Map<String, String> methods, List<String> imports) { - this(scope, nullHandling, isBinaryCommutative, isRandom, registeredNames, parameters, returnValue, workspaceVars, methods, imports, FunctionCostCategory.getDefault()); + this(scope, nullHandling, isBinaryCommutative, isRandom, registeredNames, parameters, returnValue, workspaceVars, methods, imports, FunctionCostCategory.getDefault(), null); } public DrillSimpleFuncHolder(FunctionScope scope, NullHandling nullHandling, boolean isBinaryCommutative, boolean isRandom, String[] registeredNames, ValueReference[] parameters, ValueReference returnValue, WorkspaceReference[] workspaceVars, - Map<String, String> methods, List<String> imports, FunctionCostCategory costCategory) { + Map<String, String> methods, List<String> imports, FunctionCostCategory costCategory, String interpreterClassName) { super(scope, nullHandling, isBinaryCommutative, isRandom, registeredNames, parameters, returnValue, workspaceVars, methods, imports, costCategory); setupBody = methods.get("setup"); evalBody = methods.get("eval"); @@ -62,6 +65,7 @@ class DrillSimpleFuncHolder extends DrillFuncHolder{ cleanupBody = methods.get("cleanup"); Preconditions.checkNotNull(evalBody); + this.interpreterClassName = interpreterClassName; } @Override @@ -69,8 +73,14 @@ class DrillSimpleFuncHolder extends DrillFuncHolder{ return false; } - @Override - public HoldingContainer renderEnd(ClassGenerator<?> g, HoldingContainer[] inputVariables, JVar[] workspaceJVars) { + public DrillSimpleFuncInterpreter createInterpreter() throws Exception { + Preconditions.checkArgument(this.interpreterClassName != null, "interpreterClassName should not be null!"); + + String className = InterpreterGenerator.PACKAGE_NAME + "." + this.interpreterClassName; + return (DrillSimpleFuncInterpreter) Class.forName(className).newInstance(); + } + + public HoldingContainer renderEnd(ClassGenerator<?> g, HoldingContainer[] inputVariables, JVar[] workspaceJVars){ //If the function's annotation specifies a parameter has to be constant expression, but the HoldingContainer //for the argument is not, then raise exception. for (int i =0; i < inputVariables.length; i++) { @@ -141,4 +151,11 @@ class DrillSimpleFuncHolder extends DrillFuncHolder{ return out; } + public String getSetupBody() { + return setupBody; + } + + public String getEvalBody() { + return evalBody; + } } http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/FunctionConverter.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/FunctionConverter.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/FunctionConverter.java index 402a6fc..0127e6e 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/FunctionConverter.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/FunctionConverter.java @@ -31,6 +31,7 @@ import javax.inject.Inject; import org.apache.drill.common.types.TypeProtos.MajorType; import org.apache.drill.common.util.FileUtils; +import org.apache.drill.exec.ExecConstants; import org.apache.drill.exec.expr.DrillFunc; import org.apache.drill.exec.expr.annotations.FunctionTemplate; import org.apache.drill.exec.expr.annotations.FunctionTemplate.FunctionScope; @@ -39,6 +40,7 @@ import org.apache.drill.exec.expr.annotations.Param; import org.apache.drill.exec.expr.annotations.Workspace; import org.apache.drill.exec.expr.fn.DrillFuncHolder.ValueReference; import org.apache.drill.exec.expr.fn.DrillFuncHolder.WorkspaceReference; +import org.apache.drill.exec.expr.fn.interpreter.InterpreterGenerator; import org.apache.drill.exec.expr.holders.ValueHolder; import org.apache.drill.exec.vector.complex.reader.FieldReader; import org.apache.drill.exec.vector.complex.writer.BaseWriter.ComplexWriter; @@ -257,7 +259,8 @@ public class FunctionConverter { return new DrillSimpleFuncHolder(template.scope(), template.nulls(), template.isBinaryCommutative(), template.isRandom(), registeredNames, - ps, outputField, works, methods, imports, template.costCategory()); + ps, outputField, works, methods, imports, template.costCategory(), + clazz.getSimpleName() + InterpreterGenerator.INTERPRETER_CLASSNAME_POSTFIX); } case SC_BOOLEAN_OPERATOR: return new DrillBooleanOPHolder(template.scope(), template.nulls(), http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/DrillFuncInterpreter.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/DrillFuncInterpreter.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/DrillFuncInterpreter.java new file mode 100644 index 0000000..3a83542 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/DrillFuncInterpreter.java @@ -0,0 +1,26 @@ +/** + * 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.drill.exec.expr.fn.interpreter; + +import org.apache.drill.exec.expr.holders.ValueHolder; +import org.apache.drill.exec.record.RecordBatch; + +public interface DrillFuncInterpreter { + +} http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/DrillSimpleFuncInterpreter.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/DrillSimpleFuncInterpreter.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/DrillSimpleFuncInterpreter.java new file mode 100644 index 0000000..e3696f0 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/DrillSimpleFuncInterpreter.java @@ -0,0 +1,30 @@ +/** + * 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.drill.exec.expr.fn.interpreter; + +import org.apache.drill.exec.expr.holders.ValueHolder; +import org.apache.drill.exec.record.RecordBatch; + +public interface DrillSimpleFuncInterpreter extends DrillFuncInterpreter { + + public void doSetup(ValueHolder[] args, RecordBatch incoming); + + public ValueHolder doEval(ValueHolder [] args) ; + +} http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterBuilder.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterBuilder.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterBuilder.java new file mode 100644 index 0000000..3dac818 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterBuilder.java @@ -0,0 +1,69 @@ +/** + * 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.drill.exec.expr.fn.interpreter; + +import org.apache.drill.common.config.DrillConfig; +import org.apache.drill.common.util.PathScanner; +import org.apache.drill.exec.ExecConstants; +import org.apache.drill.exec.expr.DrillFunc; +import org.apache.drill.exec.expr.fn.DrillFuncHolder; +import org.apache.drill.exec.expr.fn.DrillSimpleFuncHolder; +import org.apache.drill.exec.expr.fn.FunctionConverter; +import org.apache.drill.exec.server.options.SystemOptionManager; + +import java.util.Set; + +public class InterpreterBuilder { + static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(InterpreterBuilder.class); + + + public static void main(String args[]) { + InterpreterBuilder builder = new InterpreterBuilder(); + if (args.length != 1) { + System.err.println("Usage: InterpreterBuilder targetSrcDir\n"); + System.exit(-1); + } + builder.build(DrillConfig.create(), args[0]); + } + + private void build(DrillConfig config, String targetSrcDir) { + FunctionConverter converter = new FunctionConverter(); + Set<Class<? extends DrillFunc>> providerClasses = PathScanner.scanForImplementations(DrillFunc.class, config.getStringList(ExecConstants.FUNCTION_PACKAGES)); + + int count = 0; + for (Class<? extends DrillFunc> clazz : providerClasses) { + try { + DrillFuncHolder holder = converter.getHolder(clazz); + + if (holder != null && holder instanceof DrillSimpleFuncHolder) { + InterpreterGenerator generator = new InterpreterGenerator((DrillSimpleFuncHolder)holder, clazz.getSimpleName() + InterpreterGenerator.INTERPRETER_CLASSNAME_POSTFIX, targetSrcDir); + generator.build(); + count ++; + } + } catch (Exception ex) { + failure("Failure while creating function interpreter.", ex, clazz); + } + } + + logger.debug("Total interpreter class generated : " + count); + } + + private void failure(String message, Throwable t, Class<?> clazz) { + logger.error("Failure loading function class [{}]. Message: {}", clazz.getName(), message, t); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterEvaluator.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..0fe36cb --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterEvaluator.java @@ -0,0 +1,334 @@ +/** + * 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.drill.exec.expr.fn.interpreter; + +import com.google.common.base.Preconditions; +import org.apache.drill.common.exceptions.DrillRuntimeException; +import org.apache.drill.common.expression.BooleanOperator; +import org.apache.drill.common.expression.FunctionHolderExpression; +import org.apache.drill.common.expression.IfExpression; +import org.apache.drill.common.expression.LogicalExpression; +import org.apache.drill.common.expression.ValueExpressions; +import org.apache.drill.common.expression.visitors.AbstractExprVisitor; +import org.apache.drill.common.types.TypeProtos; +import org.apache.drill.exec.expr.DrillFuncHolderExpr; +import org.apache.drill.exec.expr.TypeHelper; +import org.apache.drill.exec.expr.ValueVectorReadExpression; +import org.apache.drill.exec.expr.annotations.FunctionTemplate; +import org.apache.drill.exec.expr.fn.DrillSimpleFuncHolder; +import org.apache.drill.exec.expr.holders.BitHolder; +import org.apache.drill.exec.expr.holders.NullableBitHolder; +import org.apache.drill.exec.expr.holders.ValueHolder; +import org.apache.drill.exec.record.RecordBatch; +import org.apache.drill.exec.vector.ValueHolderHelper; +import org.apache.drill.exec.vector.ValueVector; + + +public class InterpreterEvaluator { + + public static void evaluate(RecordBatch incoming, ValueVector outVV, LogicalExpression expr) { + + InterpreterInitVisitor initVisitor = new InterpreterInitVisitor(); + InterEvalVisitor evalVisitor = new InterEvalVisitor(incoming); + + expr.accept(initVisitor, incoming); + + for (int i = 0; i < incoming.getRecordCount(); i++) { + ValueHolder out = expr.accept(evalVisitor, i); + TypeHelper.setValueSafe(outVV, i, out); + } + + outVV.getMutator().setValueCount(incoming.getRecordCount()); + } + + public static class InterpreterInitVisitor extends AbstractExprVisitor<LogicalExpression, RecordBatch, RuntimeException> { + @Override + public LogicalExpression visitFunctionHolderExpression(FunctionHolderExpression holderExpr, RecordBatch incoming) { + if (! (holderExpr.getHolder() instanceof DrillSimpleFuncHolder)) { + throw new UnsupportedOperationException("Only Drill simple UDF can be used in interpreter mode!"); + } + + DrillSimpleFuncHolder holder = (DrillSimpleFuncHolder) holderExpr.getHolder(); + + for (int i = 0; i < holderExpr.args.size(); i++) { + holderExpr.args.get(i).accept(this, incoming); + } + + try { + DrillSimpleFuncInterpreter interpreter = holder.createInterpreter(); + + ((DrillFuncHolderExpr) holderExpr).setInterpreter(interpreter); + + return holderExpr; + + } catch (Exception ex) { + throw new RuntimeException("Error in evaluating function of " + holderExpr.getName() + ": " + ex); + } + } + + @Override + public LogicalExpression visitUnknown(LogicalExpression e, RecordBatch incoming) throws RuntimeException { + for (LogicalExpression child : e) { + child.accept(this, incoming); + } + + return e; + } + } + + + public static class InterEvalVisitor extends AbstractExprVisitor<ValueHolder, Integer, RuntimeException> { + private RecordBatch incoming; + + protected InterEvalVisitor(RecordBatch incoming) { + super(); + this.incoming = incoming; + } + + @Override + public ValueHolder visitFunctionHolderExpression(FunctionHolderExpression holderExpr, Integer inIndex) { + if (! (holderExpr.getHolder() instanceof DrillSimpleFuncHolder)) { + throw new UnsupportedOperationException("Only Drill simple UDF can be used in interpreter mode!"); + } + + DrillSimpleFuncHolder holder = (DrillSimpleFuncHolder) holderExpr.getHolder(); + + ValueHolder [] args = new ValueHolder [holderExpr.args.size()]; + for (int i = 0; i < holderExpr.args.size(); i++) { + args[i] = holderExpr.args.get(i).accept(this, inIndex); + // 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) { + // Case 1.1 : argument is null, return null value holder directly. + if (TypeHelper.isNull(args[i])) { + return TypeHelper.createValueHolder(holderExpr.getMajorType()); + } else { + // Case 1.2: argument is nullable but not null value, deNullify it. + args[i] = TypeHelper.deNullify(args[i]); + } + } else if (holder.getParameters()[i].getType().getMode() == TypeProtos.DataMode.OPTIONAL && TypeHelper.getValueHolderType(args[i]).getMode() == TypeProtos.DataMode.REQUIRED) { + // Case 2: parameter is nullable, argument is non-nullable. Nullify it. + args[i] = TypeHelper.nullify(args[i]); + } + } + } + + try { + DrillSimpleFuncInterpreter interpreter = ((DrillFuncHolderExpr) holderExpr).getInterpreter(); + + Preconditions.checkArgument(interpreter != null, "interpreter could not be null when use interpreted model to evaluate function " + holder.getRegisteredNames()[0]); + + interpreter.doSetup(args, incoming); + ValueHolder out = interpreter.doEval(args); + + if (TypeHelper.getValueHolderType(out).getMode() == TypeProtos.DataMode.OPTIONAL && + holderExpr.getMajorType().getMode() == TypeProtos.DataMode.REQUIRED) { + return TypeHelper.deNullify(out); + } else if (TypeHelper.getValueHolderType(out).getMode() == TypeProtos.DataMode.REQUIRED && + holderExpr.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL) { + return TypeHelper.nullify(out); + } else { + return out; + } + + } catch (Exception ex) { + throw new RuntimeException("Error in evaluating function of " + holderExpr.getName() + ": " + ex); + } + + } + + @Override + public ValueHolder visitBooleanOperator(BooleanOperator op, Integer inIndex) { + // Apply short circuit evaluation to boolean operator. + if (op.getName().equals("booleanAnd")) { + return visitBooleanAnd(op, inIndex); + }else if(op.getName().equals("booleanOr")) { + return visitBooleanOr(op, inIndex); + } else { + throw new UnsupportedOperationException("BooleanOperator can only be booleanAnd, booleanOr. You are using " + op.getName()); + } + } + + @Override + public ValueHolder visitIfExpression(IfExpression ifExpr, Integer inIndex) throws RuntimeException { + ValueHolder condHolder = ifExpr.ifCondition.condition.accept(this, inIndex); + + assert (condHolder instanceof BitHolder || condHolder instanceof NullableBitHolder); + + Trivalent flag = isBitOn(condHolder); + + switch (flag) { + case TRUE: + return ifExpr.ifCondition.expression.accept(this, inIndex); + case FALSE: + case NULL: + return ifExpr.elseExpression.accept(this, inIndex); + default: + throw new UnsupportedOperationException("No other possible choice. Something is not right"); + } + } + + @Override + public ValueHolder visitIntConstant(ValueExpressions.IntExpression e, Integer inIndex) throws RuntimeException { + return ValueHolderHelper.getIntHolder(e.getInt()); + } + + @Override + public ValueHolder visitFloatConstant(ValueExpressions.FloatExpression fExpr, Integer value) throws RuntimeException { + return ValueHolderHelper.getFloat4Holder(fExpr.getFloat()); + } + + @Override + public ValueHolder visitLongConstant(ValueExpressions.LongExpression intExpr, Integer value) throws RuntimeException { + return ValueHolderHelper.getBigIntHolder(intExpr.getLong()); + } + + @Override + public ValueHolder visitDoubleConstant(ValueExpressions.DoubleExpression dExpr, Integer value) throws RuntimeException { + return ValueHolderHelper.getFloat8Holder(dExpr.getDouble()); + } + + @Override + public ValueHolder visitQuotedStringConstant(ValueExpressions.QuotedString e, Integer value) throws RuntimeException { + return ValueHolderHelper.getVarCharHolder((incoming).getContext().getManagedBuffer(), e.value); + } + + + @Override + public ValueHolder visitUnknown(LogicalExpression e, Integer inIndex) throws RuntimeException { + if (e instanceof ValueVectorReadExpression) { + return visitValueVectorReadExpression((ValueVectorReadExpression) e, inIndex); + } else { + return super.visitUnknown(e, inIndex); + } + + } + + protected ValueHolder visitValueVectorReadExpression(ValueVectorReadExpression e, Integer inIndex) + throws RuntimeException { + TypeProtos.MajorType type = e.getMajorType(); + + ValueVector vv; + ValueHolder holder; + try { + switch (type.getMode()) { + case OPTIONAL: + case REQUIRED: + vv = incoming.getValueAccessorById(TypeHelper.getValueVectorClass(type.getMinorType(),type.getMode()), e.getFieldId().getFieldIds()).getValueVector(); + holder = TypeHelper.getValue(vv, inIndex.intValue()); + return holder; + default: + throw new UnsupportedOperationException("Type of " + type + " is not supported yet!"); + } + } catch (Exception ex){ + throw new DrillRuntimeException("Error when evaluate a ValueVectorReadExpression: " + ex); + } + } + + // Use Kleene algebra for three-valued logic : + // value of boolean "and" when one side is null + // p q p and q + // true null null + // false null false + // null true null + // null false false + // null null null + // "and" : 1) if any argument is false, return false. false is earlyExitValue. + // 2) if none argument is false, but at least one is null, return null. + // 3) finally, return true (finalValue). + private ValueHolder visitBooleanAnd(BooleanOperator op, Integer inIndex) { + ValueHolder [] args = new ValueHolder [op.args.size()]; + boolean hasNull = false; + ValueHolder out = null; + for (int i = 0; i < op.args.size(); i++) { + args[i] = op.args.get(i).accept(this, inIndex); + + Trivalent flag = isBitOn(args[i]); + + switch (flag) { + case FALSE: + return op.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL? TypeHelper.nullify(ValueHolderHelper.getBitHolder(0)) : ValueHolderHelper.getBitHolder(0); + case NULL: + hasNull = true; + case TRUE: + } + } + + if (hasNull) { + return ValueHolderHelper.getNullableBitHolder(true, 0); + } else { + return op.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL? TypeHelper.nullify(ValueHolderHelper.getBitHolder(1)) : ValueHolderHelper.getBitHolder(1); + } + } + + // value of boolean "or" when one side is null + // p q p and q + // true null true + // false null null + // null true true + // null false null + // null null null + private ValueHolder visitBooleanOr(BooleanOperator op, Integer inIndex) { + ValueHolder [] args = new ValueHolder [op.args.size()]; + boolean hasNull = false; + ValueHolder out = null; + for (int i = 0; i < op.args.size(); i++) { + args[i] = op.args.get(i).accept(this, inIndex); + + Trivalent flag = isBitOn(args[i]); + + switch (flag) { + case TRUE: + return op.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL? TypeHelper.nullify(ValueHolderHelper.getBitHolder(1)) : ValueHolderHelper.getBitHolder(1); + case NULL: + hasNull = true; + case FALSE: + } + } + + if (hasNull) { + return ValueHolderHelper.getNullableBitHolder(true, 0); + } else { + return op.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL? TypeHelper.nullify(ValueHolderHelper.getBitHolder(0)) : ValueHolderHelper.getBitHolder(0); + } + } + + public enum Trivalent { + FALSE, + TRUE, + NULL + } + + private Trivalent isBitOn(ValueHolder holder) { + assert (holder instanceof BitHolder || holder instanceof NullableBitHolder); + + if ( (holder instanceof BitHolder && ((BitHolder) holder).value == 1)) { + return Trivalent.TRUE; + } else if (holder instanceof NullableBitHolder && ((NullableBitHolder) holder).isSet == 1 && ((NullableBitHolder) holder).value == 1) { + return Trivalent.TRUE; + } else if (holder instanceof NullableBitHolder && ((NullableBitHolder) holder).isSet == 0) { + return Trivalent.NULL; + } else { + return Trivalent.FALSE; + } + } + } + +} + http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterGenerator.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterGenerator.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterGenerator.java new file mode 100644 index 0000000..6cede33 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/interpreter/InterpreterGenerator.java @@ -0,0 +1,143 @@ +/** + * 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.drill.exec.expr.fn.interpreter; + +import com.google.common.collect.Maps; +import com.sun.codemodel.JBlock; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JFieldVar; +import com.sun.codemodel.JMethod; +import com.sun.codemodel.JMod; +import com.sun.codemodel.JType; +import com.sun.codemodel.JVar; +import org.apache.drill.exec.expr.TypeHelper; +import org.apache.drill.exec.expr.fn.DrillFuncHolder; +import org.apache.drill.exec.expr.fn.DrillSimpleFuncHolder; +import org.apache.drill.exec.expr.holders.ValueHolder; +import org.apache.drill.exec.record.RecordBatch; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +public class InterpreterGenerator { + static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(InterpreterGenerator.class); + + public static final String PACKAGE_NAME = "org.apache.drill.exec.fn.interpreter.generated"; + private static final String ARG_NAME = "args"; + private static final String SETUP_METHOD = "doSetup"; + private static final String EVAL_METHOD = "doEval"; + public static final String INTERPRETER_CLASSNAME_POSTFIX = "Interpreter"; + + private JCodeModel model ; + private DrillSimpleFuncHolder holder; + private JDefinedClass clazz; + private String className; + private String genSourceDir; + + public InterpreterGenerator (DrillSimpleFuncHolder holder, String className, String genSourceDir) { + this.model = new JCodeModel(); + this.holder = holder; + this.className = className; + this.genSourceDir = genSourceDir; + } + + public void build() throws Exception { + try { + this.clazz = model._class(PACKAGE_NAME + "." + className); + JClass iface = model.ref(DrillSimpleFuncInterpreter.class); + clazz._implements(iface); + + Map<DrillFuncHolder.WorkspaceReference, JFieldVar> wsFieldVars = Maps.newHashMap(); + for (DrillFuncHolder.WorkspaceReference ws : holder.getWorkspaceVars()) { + JFieldVar wsVar = clazz.field(JMod.PRIVATE, ws.getType(), ws.getName()); + wsFieldVars.put(ws, wsVar); + } + + generateSetup(wsFieldVars); + + generateEval(); + + // generate the java source code for the function. + File file = new File(genSourceDir); + if (!file.exists()) { + file.mkdir(); + } + + model.build(file); + + } catch (Exception e) { + logger.error("Error when generate code for " + className + " error " + e); + throw new IllegalStateException(e); + } + } + + private void generateSetup(Map<DrillFuncHolder.WorkspaceReference, JFieldVar> wsFieldVars) { + JClass valueholderClass = model.ref(ValueHolder.class); + + JMethod doSetupMethod = clazz.method(JMod.PUBLIC, Void.TYPE, SETUP_METHOD); + doSetupMethod.param(valueholderClass.array(), ARG_NAME); + JVar incomingJVar = doSetupMethod.param(model.ref(RecordBatch.class), "incoming"); + + if (holder.getSetupBody()!=null && ! holder.getSetupBody().trim().equals("{}")) { + declareAssignParm(model, doSetupMethod.body(), holder, ARG_NAME, true); + } + + for (DrillFuncHolder.WorkspaceReference ws : holder.getWorkspaceVars()) { + if (ws.isInject()) { + doSetupMethod.body().assign(wsFieldVars.get(ws), incomingJVar.invoke("getContext").invoke("getManagedBuffer")); + } + } + + doSetupMethod.body().directStatement(holder.getSetupBody()); + } + + private void generateEval() { + JClass valueholderClass = model.ref(ValueHolder.class); + + JMethod doEvalMethod = clazz.method(JMod.PUBLIC, ValueHolder.class, EVAL_METHOD); + doEvalMethod.param(valueholderClass.array(), ARG_NAME); + + if (holder.getEvalBody()!=null && ! holder.getEvalBody().trim().equals("")) { + declareAssignParm(model, doEvalMethod.body(), holder, ARG_NAME, false); + } + + DrillFuncHolder.ValueReference returnValue = holder.getReturnValue(); + JType outType = TypeHelper.getHolderType(model, returnValue.getType().getMinorType(), returnValue.getType().getMode()); + + JVar outVar = doEvalMethod.body().decl(outType, returnValue.getName(), JExpr._new(outType)); + + doEvalMethod.body().directStatement(holder.getEvalBody()); + doEvalMethod.body()._return(outVar); + } + + private void declareAssignParm(JCodeModel model, JBlock block, DrillFuncHolder holder, String argName, boolean constantOnly) { + + int index = 0; + DrillFuncHolder.ValueReference[] parameters = holder.getParameters(); + + for (DrillFuncHolder.ValueReference parm : parameters) { + JType type = TypeHelper.getHolderType(model, parm.getType().getMinorType(), parm.getType().getMode()); + block.decl(type, parm.getName(), JExpr.cast(type, JExpr.component(JExpr.ref(argName), JExpr.lit(index++)))); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/java-exec/src/main/java/org/apache/drill/exec/vector/ValueHolderHelper.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/ValueHolderHelper.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/ValueHolderHelper.java index 31afb6f..2f5bf6a 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/ValueHolderHelper.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/ValueHolderHelper.java @@ -21,11 +21,17 @@ import io.netty.buffer.DrillBuf; import java.math.BigDecimal; +import org.apache.drill.exec.expr.holders.BigIntHolder; +import org.apache.drill.exec.expr.holders.BitHolder; import org.apache.drill.exec.expr.holders.Decimal18Holder; import org.apache.drill.exec.expr.holders.Decimal28SparseHolder; import org.apache.drill.exec.expr.holders.Decimal38SparseHolder; import org.apache.drill.exec.expr.holders.Decimal9Holder; +import org.apache.drill.exec.expr.holders.Float4Holder; +import org.apache.drill.exec.expr.holders.Float8Holder; +import org.apache.drill.exec.expr.holders.IntHolder; import org.apache.drill.exec.expr.holders.IntervalDayHolder; +import org.apache.drill.exec.expr.holders.NullableBitHolder; import org.apache.drill.exec.expr.holders.VarCharHolder; import org.apache.drill.exec.memory.BufferAllocator; import org.apache.drill.exec.util.DecimalUtility; @@ -36,6 +42,52 @@ import com.google.common.base.Charsets; public class ValueHolderHelper { static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ValueHolderHelper.class); + public static IntHolder getIntHolder(int value) { + IntHolder holder = new IntHolder(); + holder.value = value; + + return holder; + } + + public static BigIntHolder getBigIntHolder(long value) { + BigIntHolder holder = new BigIntHolder(); + holder.value = value; + + return holder; + } + + public static Float4Holder getFloat4Holder(float value) { + Float4Holder holder = new Float4Holder(); + holder.value = value; + + return holder; + } + + public static Float8Holder getFloat8Holder(double value) { + Float8Holder holder = new Float8Holder(); + holder.value = value; + + return holder; + } + + + public static BitHolder getBitHolder(int value) { + BitHolder holder = new BitHolder(); + holder.value = value; + + return holder; + } + + public static NullableBitHolder getNullableBitHolder(boolean isNull, int value) { + NullableBitHolder holder = new NullableBitHolder(); + holder.isSet = isNull? 0 : 1; + if (! isNull) { + holder.value = value; + } + + return holder; + } + public static VarCharHolder getVarCharHolder(DrillBuf buf, String s){ VarCharHolder vch = new VarCharHolder(); http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/9bc71fc5/exec/pom.xml ---------------------------------------------------------------------- diff --git a/exec/pom.xml b/exec/pom.xml index fe2a75e..79ed482 100644 --- a/exec/pom.xml +++ b/exec/pom.xml @@ -34,5 +34,6 @@ <module>java-exec</module> <module>jdbc</module> <module>jdbc-all</module> + <module>interpreter</module> </modules> </project>