| /* |
| * 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 java.math.BigDecimal; |
| import java.time.LocalDate; |
| import java.time.LocalDateTime; |
| import java.time.LocalTime; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Stack; |
| |
| import io.netty.buffer.DrillBuf; |
| import org.apache.calcite.util.Pair; |
| import org.apache.drill.common.FunctionNames; |
| import org.apache.drill.common.expression.AnyValueExpression; |
| import org.apache.drill.common.expression.BooleanOperator; |
| import org.apache.drill.common.expression.CastExpression; |
| import org.apache.drill.common.expression.ConvertExpression; |
| import org.apache.drill.common.expression.ExpressionStringBuilder; |
| import org.apache.drill.common.expression.FunctionCall; |
| import org.apache.drill.common.expression.FunctionHolderExpression; |
| import org.apache.drill.common.expression.IfExpression; |
| import org.apache.drill.common.expression.IfExpression.IfCondition; |
| import org.apache.drill.common.expression.LogicalExpression; |
| import org.apache.drill.common.expression.NullExpression; |
| import org.apache.drill.common.expression.PathSegment; |
| import org.apache.drill.common.expression.SchemaPath; |
| import org.apache.drill.common.expression.TypedNullConstant; |
| import org.apache.drill.common.expression.ValueExpressions; |
| import org.apache.drill.common.expression.ValueExpressions.BooleanExpression; |
| import org.apache.drill.common.expression.ValueExpressions.DateExpression; |
| import org.apache.drill.common.expression.ValueExpressions.Decimal18Expression; |
| import org.apache.drill.common.expression.ValueExpressions.Decimal28Expression; |
| import org.apache.drill.common.expression.ValueExpressions.Decimal38Expression; |
| import org.apache.drill.common.expression.ValueExpressions.Decimal9Expression; |
| import org.apache.drill.common.expression.ValueExpressions.DoubleExpression; |
| import org.apache.drill.common.expression.ValueExpressions.FloatExpression; |
| import org.apache.drill.common.expression.ValueExpressions.IntExpression; |
| import org.apache.drill.common.expression.ValueExpressions.IntervalDayExpression; |
| import org.apache.drill.common.expression.ValueExpressions.IntervalYearExpression; |
| import org.apache.drill.common.expression.ValueExpressions.LongExpression; |
| import org.apache.drill.common.expression.ValueExpressions.QuotedString; |
| import org.apache.drill.common.expression.ValueExpressions.TimeExpression; |
| import org.apache.drill.common.expression.ValueExpressions.TimeStampExpression; |
| import org.apache.drill.common.expression.ValueExpressions.VarDecimalExpression; |
| import org.apache.drill.common.expression.visitors.AbstractExprVisitor; |
| import org.apache.drill.common.types.TypeProtos; |
| import org.apache.drill.common.types.TypeProtos.MajorType; |
| import org.apache.drill.common.types.Types; |
| import org.apache.drill.exec.compile.sig.ConstantExpressionIdentifier; |
| import org.apache.drill.exec.compile.sig.GeneratorMapping; |
| import org.apache.drill.exec.compile.sig.MappingSet; |
| import org.apache.drill.exec.expr.ClassGenerator.HoldingContainer; |
| import org.apache.drill.exec.expr.annotations.FunctionTemplate; |
| import org.apache.drill.exec.expr.fn.AbstractFuncHolder; |
| import org.apache.drill.exec.expr.holders.ValueHolder; |
| import org.apache.drill.exec.physical.impl.filter.ReturnValueExpression; |
| import org.apache.drill.exec.vector.ValueHolderHelper; |
| import org.apache.drill.exec.vector.complex.reader.FieldReader; |
| |
| import com.google.common.base.Function; |
| import com.google.common.collect.Maps; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.sun.codemodel.JBlock; |
| import com.sun.codemodel.JClass; |
| import com.sun.codemodel.JConditional; |
| import com.sun.codemodel.JExpr; |
| import com.sun.codemodel.JExpression; |
| import com.sun.codemodel.JFieldRef; |
| import com.sun.codemodel.JInvocation; |
| import com.sun.codemodel.JLabel; |
| import com.sun.codemodel.JType; |
| import com.sun.codemodel.JVar; |
| |
| /** |
| * Visitor that generates code for eval |
| */ |
| public class EvaluationVisitor { |
| private static final Logger logger = LoggerFactory.getLogger(EvaluationVisitor.class); |
| |
| /** |
| * Callback function for the expression visitor |
| */ |
| private interface VisitorCallback { |
| HoldingContainer getHolder(); |
| } |
| |
| private static class ExpressionHolder { |
| private final LogicalExpression expression; |
| private final GeneratorMapping mapping; |
| private final MappingSet mappingSet; |
| |
| ExpressionHolder(LogicalExpression expression, MappingSet mappingSet) { |
| this.expression = expression; |
| this.mapping = mappingSet.getCurrentMapping(); |
| this.mappingSet = mappingSet; |
| } |
| |
| @Override |
| public int hashCode() { |
| return expression.accept(new HashVisitor(), null); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (!(obj instanceof ExpressionHolder)) { |
| return false; |
| } |
| ExpressionHolder that = (ExpressionHolder) obj; |
| return this.mappingSet == that.mappingSet && this.mapping == that.mapping && expression.accept(new EqualityVisitor(), that.expression); |
| } |
| } |
| |
| /** |
| * Extended variable descriptor ("holding container") for the variable |
| * which references the value holder ("FooHolder") that stores the value |
| * from a value vector. Used to pass along the original value vector |
| * variable along with the value holder. In particular, this class allows |
| * "time travel": to retroactively generate a reader for a union once |
| * we realize we need the reader. |
| * <p> |
| * TODO: This is not especially elegant. But the code that declares the |
| * holder/reader does not know about the parameter(s) that will use it, |
| * and the normal <code>HoldingContainer</code> can hold only one variable, |
| * not a broader context. This version holds more context. Perhaps this |
| * idea should be generalized. |
| */ |
| public static class VectorVariableHolder extends HoldingContainer { |
| |
| private final ClassGenerator<?> classGen; |
| private final JBlock vvSetupBlock; |
| private final int insertPosn; |
| private final JExpression vectorExpr; |
| private final JExpression recordIndex; |
| private JVar readerVar; |
| |
| public VectorVariableHolder(HoldingContainer base, ClassGenerator<?> classGen, |
| JBlock vvSetupBlock, JExpression vectorExpr, JExpression recordIndex) { |
| super(base); |
| this.classGen = classGen; |
| this.vvSetupBlock = vvSetupBlock; |
| this.vectorExpr = vectorExpr; |
| this.recordIndex = recordIndex; |
| insertPosn = vvSetupBlock.pos(); |
| } |
| |
| /** |
| * Specialized hack for the UNION type to obtain a <code>FieldReader</code> |
| * directly from the value vector, bypassing the <code>UnionHolder</code> |
| * created from that vector. Handles setting the reader position only once |
| * in an eval block rather than on each reference. There may be multiple |
| * functions that need the reader. To ensure we create only one common |
| * reader, we "go back in time" to add the reader at the point after |
| * we declared the <code>UnionHolder</code>. |
| */ |
| public JVar generateUnionReader() { |
| if (readerVar == null) { |
| createReaderVar(); |
| } |
| return readerVar; |
| } |
| |
| private void createReaderVar() { |
| // Rewind to where we added the UnionHolder |
| int curPosn = vvSetupBlock.pos(); |
| vvSetupBlock.pos(insertPosn); |
| |
| // UnionReader readerx = vvy.getReader(); |
| JType readerClass = classGen.getModel()._ref(FieldReader.class); |
| JExpression expr = vectorExpr.invoke("getReader"); |
| readerVar = vvSetupBlock.decl(readerClass, classGen.getNextVar("reader"), expr); |
| |
| // readerx.setPosition(indexExpr); |
| JInvocation setPosnStmt = readerVar.invoke("setPosition").arg(recordIndex); |
| vvSetupBlock.add(setPosnStmt); |
| |
| // Put the insert position back to where it was; adjusting |
| // for the statements just added. |
| int offset = vvSetupBlock.pos() - insertPosn; |
| vvSetupBlock.pos(curPosn + offset); |
| } |
| } |
| |
| public Map<ExpressionHolder, HoldingContainer> previousExpressions = new HashMap<>(); |
| private final Stack<Map<ExpressionHolder, HoldingContainer>> mapStack = new Stack<>(); |
| |
| public HoldingContainer addExpr(LogicalExpression e, ClassGenerator<?> generator) { |
| Set<LogicalExpression> constantBoundaries; |
| if (generator.getMappingSet().hasEmbeddedConstant()) { |
| constantBoundaries = Collections.emptySet(); |
| } else { |
| constantBoundaries = ConstantExpressionIdentifier.getConstantExpressionSet(e); |
| } |
| return e.accept(new CSEFilter(constantBoundaries), generator); |
| } |
| |
| void newScope() { |
| mapStack.push(previousExpressions); |
| previousExpressions = Maps.newHashMap(); |
| } |
| |
| void leaveScope() { |
| previousExpressions.clear(); |
| previousExpressions = mapStack.pop(); |
| } |
| |
| /** |
| * Get a HoldingContainer for the expression if it had been already evaluated |
| */ |
| private HoldingContainer getPrevious(LogicalExpression expression, MappingSet mappingSet) { |
| ExpressionHolder holder = new ExpressionHolder(expression, mappingSet); |
| HoldingContainer previous = null; |
| for (Map<ExpressionHolder,HoldingContainer> m : mapStack) { |
| previous = m.get(holder); |
| if (previous != null) { |
| break; |
| } |
| } |
| if (previous == null) { |
| previous = previousExpressions.get(holder); |
| } |
| if (previous != null) { |
| logger.debug("Found previously evaluated expression: {}", ExpressionStringBuilder.toString(expression)); |
| } |
| return previous; |
| } |
| |
| private void put(LogicalExpression expression, HoldingContainer hc, MappingSet mappingSet) { |
| previousExpressions.put(new ExpressionHolder(expression, mappingSet), hc); |
| } |
| |
| private static class EvalVisitor extends AbstractExprVisitor<HoldingContainer, ClassGenerator<?>, RuntimeException> { |
| |
| @Override |
| public HoldingContainer visitFunctionCall(FunctionCall call, ClassGenerator<?> generator) throws RuntimeException { |
| throw new UnsupportedOperationException("FunctionCall is not expected here. " |
| + "It should have been converted to FunctionHolderExpression in materialization"); |
| } |
| |
| @Override |
| public HoldingContainer visitBooleanOperator(BooleanOperator op, |
| ClassGenerator<?> generator) throws RuntimeException { |
| if (op.getName().equals(FunctionNames.AND)) { |
| return visitBooleanAnd(op, generator); |
| } else if(op.getName().equals(FunctionNames.OR)) { |
| return visitBooleanOr(op, generator); |
| } else { |
| throw new UnsupportedOperationException( |
| "BooleanOperator can only be booleanAnd, booleanOr. You are using " + op.getName()); |
| } |
| } |
| |
| /** |
| * Generate a function call (which actually in-lines the function within |
| * the generated code.) |
| */ |
| @Override |
| public HoldingContainer visitFunctionHolderExpression(FunctionHolderExpression holderExpr, |
| ClassGenerator<?> generator) throws RuntimeException { |
| |
| // Reference to the function definition. |
| AbstractFuncHolder fnHolder = (AbstractFuncHolder) holderExpr.getHolder(); |
| |
| // Define (internal) workspace variables |
| JVar[] workspaceVars = fnHolder.renderStart(generator, null, holderExpr.getFieldReference()); |
| |
| if (fnHolder.isNested()) { |
| generator.getMappingSet().enterChild(); |
| } |
| |
| // Create (or obtain) value holders for each of the actual function |
| // arguments. In many cases (all? some?), the function reference |
| // (holder expr) represents arguments as value holders. |
| HoldingContainer[] args = new HoldingContainer[holderExpr.args.size()]; |
| for (int i = 0; i < holderExpr.args.size(); i++) { |
| args[i] = holderExpr.args.get(i).accept(this, generator); |
| } |
| |
| // Aggregate functions generate the per-value code here. |
| fnHolder.renderMiddle(generator, args, workspaceVars); |
| |
| if (fnHolder.isNested()) { |
| generator.getMappingSet().exitChild(); |
| } |
| |
| // Simple functions generate per-value code here. |
| // Aggregate functions generate the per-group aggregate here. |
| return fnHolder.renderEnd(generator, args, workspaceVars, holderExpr); |
| } |
| |
| @Override |
| public HoldingContainer visitIfExpression(IfExpression ifExpr, ClassGenerator<?> generator) throws RuntimeException { |
| JBlock local = generator.getEvalBlock(); |
| |
| HoldingContainer output = generator.declare(ifExpr.getMajorType()); |
| |
| JBlock conditionalBlock = new JBlock(false, false); |
| IfCondition c = ifExpr.ifCondition; |
| |
| HoldingContainer holdingContainer = c.condition.accept(this, generator); |
| JConditional jc; |
| if (holdingContainer.isOptional()) { |
| jc = conditionalBlock._if(holdingContainer.getIsSet().eq(JExpr.lit(1)).cand(holdingContainer.getValue().eq(JExpr.lit(1)))); |
| } else { |
| jc = conditionalBlock._if(holdingContainer.getValue().eq(JExpr.lit(1))); |
| } |
| |
| generator.nestEvalBlock(jc._then()); |
| |
| HoldingContainer thenExpr = c.expression.accept(this, generator); |
| |
| generator.unNestEvalBlock(); |
| |
| List<String> holderFields = ValueHolderHelper.getHolderParams(output.getMajorType()); |
| if (thenExpr.isOptional()) { |
| JConditional newCond = jc._then()._if(thenExpr.getIsSet().ne(JExpr.lit(0))); |
| JBlock b = newCond._then(); |
| for (String holderField : holderFields) { |
| b.assign(output.f(holderField), thenExpr.f(holderField)); |
| } |
| } else { |
| for (String holderField : holderFields) { |
| jc._then().assign(output.f(holderField), thenExpr.f(holderField)); |
| } |
| } |
| |
| generator.nestEvalBlock(jc._else()); |
| |
| HoldingContainer elseExpr = ifExpr.elseExpression.accept(this, generator); |
| |
| generator.unNestEvalBlock(); |
| |
| if (elseExpr.isOptional()) { |
| JConditional newCond = jc._else()._if(elseExpr.getIsSet().ne(JExpr.lit(0))); |
| JBlock b = newCond._then(); |
| for (String holderField : holderFields) { |
| b.assign(output.f(holderField), elseExpr.f(holderField)); |
| } |
| } else { |
| for (String holderField : holderFields) { |
| jc._else().assign(output.f(holderField), elseExpr.f(holderField)); |
| } |
| } |
| local.add(conditionalBlock); |
| return output; |
| } |
| |
| @Override |
| public HoldingContainer visitSchemaPath(SchemaPath path, ClassGenerator<?> generator) throws RuntimeException { |
| throw new UnsupportedOperationException("All schema paths should have been replaced with ValueVectorExpressions."); |
| } |
| |
| private HoldingContainer getHoldingContainer(ClassGenerator<?> generator, |
| MajorType majorType, |
| Function<DrillBuf, ? extends ValueHolder> function) { |
| JType holderType = generator.getHolderType(majorType); |
| Pair<Integer, JVar> depthVar = generator.declareClassConstField("const", holderType, function); |
| JFieldRef outputSet = null; |
| JVar var = depthVar.getValue(); |
| if (majorType.getMode() == TypeProtos.DataMode.OPTIONAL) { |
| outputSet = var.ref("isSet"); |
| } |
| return new HoldingContainer(majorType, var, var.ref("value"), outputSet); |
| } |
| |
| @Override |
| public HoldingContainer visitLongConstant(LongExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getBigIntHolder(e.getLong())); |
| } |
| |
| @Override |
| public HoldingContainer visitIntConstant(IntExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getIntHolder(e.getInt())); |
| } |
| |
| @Override |
| public HoldingContainer visitDateConstant(DateExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getDateHolder(e.getDate())); |
| } |
| |
| @Override |
| public HoldingContainer visitTimeConstant(TimeExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getTimeHolder(e.getTime())); |
| } |
| |
| @Override |
| public HoldingContainer visitIntervalYearConstant(IntervalYearExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getIntervalYearHolder(e.getIntervalYear())); |
| } |
| |
| @Override |
| public HoldingContainer visitTimeStampConstant(TimeStampExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getTimeStampHolder(e.getTimeStamp())); |
| } |
| |
| @Override |
| public HoldingContainer visitFloatConstant(FloatExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getFloat4Holder(e.getFloat())); |
| } |
| |
| @Override |
| public HoldingContainer visitDoubleConstant(DoubleExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getFloat8Holder(e.getDouble())); |
| } |
| |
| @Override |
| public HoldingContainer visitBooleanConstant(BooleanExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getBitHolder(e.getBoolean() ? 1 : 0)); |
| } |
| |
| /** |
| * Handles implementation-specific expressions not known to the visitor |
| * mechanism. |
| */ |
| // TODO: Consider adding these "unknown" expressions to the visitor class. |
| @Override |
| public HoldingContainer visitUnknown(LogicalExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| if (e instanceof ValueVectorReadExpression) { |
| return visitValueVectorReadExpression((ValueVectorReadExpression) e, generator); |
| } else if (e instanceof ValueVectorWriteExpression) { |
| return visitValueVectorWriteExpression((ValueVectorWriteExpression) e, generator); |
| } else if (e instanceof ReturnValueExpression) { |
| return visitReturnValueExpression((ReturnValueExpression) e, generator); |
| } else if (e instanceof HoldingContainerExpression) { |
| // Expression contains a "holder", just return it. |
| return ((HoldingContainerExpression) e).getContainer(); |
| } else if (e instanceof NullExpression) { |
| return generator.declare(e.getMajorType()); |
| } else if (e instanceof TypedNullConstant) { |
| return generator.declare(e.getMajorType()); |
| } else { |
| return super.visitUnknown(e, generator); |
| } |
| } |
| |
| /** |
| * <p> |
| * Creates local variable based on given parameter type and name and assigns |
| * parameter to this local instance. |
| * </p> |
| * |
| * <p> |
| * Example: <br/> |
| * IntHolder seedValue0 = new IntHolder();<br/> |
| * seedValue0 .value = seedValue; |
| * </p> |
| * |
| * @param e parameter expression |
| * @param generator class generator |
| * @return holder instance |
| */ |
| @Override |
| public HoldingContainer visitParameter(ValueExpressions.ParameterExpression e, ClassGenerator<?> generator) { |
| HoldingContainer out = generator.declare(e.getMajorType(), e.getName(), true); |
| generator.getEvalBlock().assign(out.getValue(), JExpr.ref(e.getName())); |
| return out; |
| } |
| |
| private HoldingContainer visitValueVectorWriteExpression(ValueVectorWriteExpression e, ClassGenerator<?> generator) { |
| |
| final LogicalExpression child = e.getChild(); |
| final HoldingContainer inputContainer = child.accept(this, generator); |
| |
| JBlock block = generator.getEvalBlock(); |
| JExpression outIndex = generator.getMappingSet().getValueWriteIndex(); |
| JVar vv = generator.declareVectorValueSetupAndMember(generator.getMappingSet().getOutgoing(), e.getFieldId()); |
| |
| // Only when the input is a reader, use writer interface to copy value. |
| // Otherwise, input is a holder and we use vv mutator to set value. |
| if (inputContainer.isReader()) { |
| JType writerImpl = generator.getModel()._ref( |
| TypeHelper.getWriterImpl(inputContainer.getMinorType(), inputContainer.getMajorType().getMode())); |
| JType writerIFace = generator.getModel()._ref( |
| TypeHelper.getWriterInterface(inputContainer.getMinorType(), inputContainer.getMajorType().getMode())); |
| JVar writer = generator.declareClassField("writer", writerIFace); |
| generator.getSetupBlock().assign(writer, JExpr._new(writerImpl).arg(vv).arg(JExpr._null())); |
| generator.getEvalBlock().add(writer.invoke("setPosition").arg(outIndex)); |
| String copyMethod = inputContainer.isSingularRepeated() ? "copyAsValueSingle" : "copyAsValue"; |
| generator.getEvalBlock().add(inputContainer.getHolder().invoke(copyMethod).arg(writer)); |
| if (e.isSafe()) { |
| HoldingContainer outputContainer = generator.declare(Types.REQUIRED_BIT); |
| generator.getEvalBlock().assign(outputContainer.getValue(), JExpr.lit(1)); |
| return outputContainer; |
| } |
| } else { |
| final JInvocation setMeth = GetSetVectorHelper.write(e.getChild().getMajorType(), vv, inputContainer, outIndex, e.isSafe() ? "setSafe" : "set"); |
| if (inputContainer.isOptional()) { |
| JConditional jc = block._if(inputContainer.getIsSet().eq(JExpr.lit(0)).not()); |
| block = jc._then(); |
| } |
| block.add(setMeth); |
| } |
| return null; |
| } |
| |
| /** |
| * Generate code to extract a value from a value vector into a local |
| * variable which can be passed to a function. The local variable is either |
| * a value holder (<code>FooHolder</code>) or a reader. In most cases, the |
| * decision is made based on the type of the vector. Primitives use value |
| * holders, complex variables use readers. |
| * <p> |
| * Code in {@link DrillFuncHolder#declareInputVariable} converts the local |
| * variable into the form requested by the </code>{@literal @}Param</code> |
| * annotation which itself can be a value holder or a reader. |
| * <p> |
| * A special case occurs for the UNION type. Drill supports conversion from |
| * a reader to a holder, but not the other way around. We prefer to use a |
| * holder. But, if the function wants a reader, we must generate a reader. |
| * If some other function wants a holder, we can convert from the reader to |
| * a holder. |
| */ |
| private HoldingContainer visitValueVectorReadExpression(ValueVectorReadExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| // declare value vector |
| DirectExpression batchName; |
| JExpression batchIndex; |
| JExpression recordIndex; |
| |
| // If value vector read expression has batch reference, use its values in |
| // generated code, otherwise use values provided by mapping set (which |
| // point to only one batch) primarily used for non-equi joins where |
| // expression conditions may refer to more than one batch |
| BatchReference batchRef = e.getBatchRef(); |
| if (batchRef != null) { |
| batchName = DirectExpression.direct(batchRef.getBatchName()); |
| batchIndex = DirectExpression.direct(batchRef.getBatchIndex()); |
| recordIndex = DirectExpression.direct(batchRef.getRecordIndex()); |
| } else { |
| batchName = generator.getMappingSet().getIncoming(); |
| batchIndex = generator.getMappingSet().getValueReadIndex(); |
| recordIndex = batchIndex; |
| } |
| |
| JExpression vv1 = generator.declareVectorValueSetupAndMember(batchName, e.getFieldId()); |
| JExpression componentVariable = batchIndex.shrz(JExpr.lit(16)); |
| if (e.isSuperReader()) { |
| vv1 = (vv1.component(componentVariable)); |
| recordIndex = recordIndex.band(JExpr.lit((int) Character.MAX_VALUE)); |
| } |
| |
| // evaluation work. |
| MajorType type = e.getMajorType(); |
| final boolean hasReadPath = e.hasReadPath(); |
| final boolean complex = Types.isComplex(type); |
| |
| if (!hasReadPath && !complex) { |
| return createHolderForVector(generator, recordIndex, vv1, type); |
| } else { |
| return createReaderForVector(e, generator, recordIndex, vv1, type); |
| } |
| } |
| |
| private HoldingContainer createHolderForVector(ClassGenerator<?> generator, |
| JExpression recordIndex, JExpression vv1, MajorType type) { |
| JBlock eval = new JBlock(); |
| HoldingContainer out = generator.declare(type); |
| if (Types.isRepeated(type)) { |
| JExpression expr = vv1.invoke("getReader"); |
| // Set correct position to the reader |
| eval.add(expr.invoke("reset")); |
| eval.add(expr.invoke("setPosition").arg(recordIndex)); |
| } |
| |
| // Generate code to read the vector into a holder |
| GetSetVectorHelper.read(type, vv1, eval, out, generator.getModel(), recordIndex); |
| JBlock evalBlock = generator.getEvalBlock(); |
| evalBlock.add(eval); |
| return new VectorVariableHolder(out, generator, evalBlock, vv1, recordIndex); |
| } |
| |
| private HoldingContainer createReaderForVector(ValueVectorReadExpression e, |
| ClassGenerator<?> generator, JExpression recordIndex, JExpression vv1, |
| MajorType type) { |
| |
| HoldingContainer out = generator.declare(type); |
| |
| final boolean repeated = Types.isRepeated(type); |
| final boolean complex = Types.isComplex(type); |
| final boolean listVector = e.getTypedFieldId().isListVector(); |
| final boolean isUnion = Types.isUnion(type); |
| |
| // Create a reader for the vector. |
| |
| JExpression expr = vv1.invoke("getReader"); |
| PathSegment seg = e.getReadPath(); |
| |
| JVar isNull = null; |
| boolean isNullReaderLikely = isNullReaderLikely(seg, complex || repeated || listVector || isUnion); |
| if (isNullReaderLikely) { |
| isNull = generator.getEvalBlock().decl(generator.getModel().INT, generator.getNextVar("isNull"), JExpr.lit(0)); |
| } |
| |
| JLabel label = generator.getEvalBlock().label("complex"); |
| JBlock eval = generator.getEvalBlock().block(); |
| |
| // Position to the correct value. |
| eval.add(expr.invoke("reset")); |
| eval.add(expr.invoke("setPosition").arg(recordIndex)); |
| int listNum = 0; |
| |
| // Variable to store index of key in dict (if there is one) |
| // if entry for the key is not found, will be assigned a value of SingleDictReaderImpl#NOT_FOUND. |
| JVar valueIndex = eval.decl(generator.getModel().INT, "valueIndex", JExpr.lit(-2)); |
| |
| int depth = 0; |
| |
| while (seg != null) { |
| if (seg.isArray()) { |
| |
| // stop once we get to the last segment and the final type is |
| // neither complex nor repeated (map, dict, list, repeated list). |
| // In case of non-complex and non-repeated type, we return Holder, |
| // in stead of FieldReader. |
| if (seg.isLastPath() && !complex && !repeated && !listVector) { |
| break; |
| } |
| |
| if (e.getFieldId().isDict(depth)) { |
| expr = getDictReaderReadByKeyExpression(generator, eval, expr, seg, valueIndex, isNull); |
| seg = seg.getChild(); |
| depth++; |
| continue; |
| } |
| |
| JVar list = generator.declareClassField("list", generator.getModel()._ref(FieldReader.class)); |
| eval.assign(list, expr); |
| |
| // If this is an array, set a single position for the expression to |
| // allow us to read the right data lower down. |
| JVar desiredIndex = eval.decl(generator.getModel().INT, "desiredIndex" + listNum, |
| JExpr.lit(seg.getArraySegment().getIndex())); |
| // start with negative one so that we are at zero after first call |
| // to next. |
| JVar currentIndex = eval.decl(generator.getModel().INT, "currentIndex" + listNum, JExpr.lit(-1)); |
| |
| eval._while( |
| currentIndex.lt(desiredIndex) |
| .cand(list.invoke("next"))).body().assign(currentIndex, currentIndex.plus(JExpr.lit(1))); |
| |
| JBlock ifNoVal = eval._if(desiredIndex.ne(currentIndex))._then().block(); |
| if (out.isOptional()) { |
| ifNoVal.assign(out.getIsSet(), JExpr.lit(0)); |
| } |
| ifNoVal.assign(isNull, JExpr.lit(1)); |
| ifNoVal._break(label); |
| |
| expr = list.invoke("reader"); |
| listNum++; |
| } else { |
| |
| if (e.getFieldId().isDict(depth)) { |
| MajorType finalType = e.getFieldId().getFinalType(); |
| if (seg.getChild() == null && !(Types.isComplex(finalType) || Types.isRepeated(finalType))) { |
| // This is the last segment: |
| eval.add(expr.invoke("read").arg(getKeyExpression(seg, generator)).arg(out.getHolder())); |
| return out; |
| } |
| |
| expr = getDictReaderReadByKeyExpression(generator, eval, expr, seg, valueIndex, isNull); |
| seg = seg.getChild(); |
| depth++; |
| continue; |
| } |
| |
| JExpression fieldName = JExpr.lit(seg.getNameSegment().getPath()); |
| expr = expr.invoke("reader").arg(fieldName); |
| } |
| seg = seg.getChild(); |
| depth++; |
| } |
| |
| // expected that after loop depth at least equal to last id index |
| depth = Math.max(depth, e.getFieldId().getFieldIds().length - 1); |
| |
| if (complex || repeated) { |
| |
| // Declare the reader for this vector |
| JVar complexReader = generator.declareClassField("reader", generator.getModel()._ref(FieldReader.class)); |
| |
| if (isNullReaderLikely) { |
| JConditional jc = generator.getEvalBlock()._if(isNull.eq(JExpr.lit(0))); |
| |
| JClass nrClass = generator.getModel().ref(org.apache.drill.exec.vector.complex.impl.NullReader.class); |
| JExpression nullReader; |
| if (complex) { |
| nullReader = nrClass.staticRef("EMPTY_MAP_INSTANCE"); |
| } else { |
| nullReader = nrClass.staticRef("EMPTY_LIST_INSTANCE"); |
| } |
| |
| jc._then().assign(complexReader, expr); |
| jc._else().assign(complexReader, nullReader); |
| } else { |
| eval.assign(complexReader, expr); |
| } |
| |
| HoldingContainer hc = new HoldingContainer(type, complexReader, null, null, false, true); |
| return hc; |
| } else { |
| |
| // For a DICT, create a holder, then obtain the reader. |
| |
| if (seg != null) { |
| JExpression holderExpr = out.getHolder(); |
| JExpression argExpr; |
| if (e.getFieldId().isDict(depth)) { |
| holderExpr = JExpr.cast(generator.getModel()._ref(ValueHolder.class), holderExpr); |
| argExpr = getKeyExpression(seg, generator); |
| } else { |
| argExpr = JExpr.lit(seg.getArraySegment().getIndex()); |
| } |
| JClass dictReaderClass = generator.getModel().ref(org.apache.drill.exec.vector.complex.impl.SingleDictReaderImpl.class); |
| JConditional jc = eval._if(valueIndex.ne(dictReaderClass.staticRef("NOT_FOUND"))); |
| jc._then().add(expr.invoke("read").arg(argExpr).arg(holderExpr)); |
| } else { |
| eval.add(expr.invoke("read").arg(out.getHolder())); |
| } |
| } |
| return out; |
| } |
| |
| /* Check if a Path expression could produce a NullReader. A path expression will produce a null reader, when: |
| * 1) It contains an array segment as non-leaf segment : a.b[2].c. segment [2] might produce null reader. |
| * 2) It contains an array segment as leaf segment, AND the final output is complex or repeated : a.b[2], when |
| * the final type of this expression is a map, or repeated list, or repeated map. |
| */ |
| private boolean isNullReaderLikely(PathSegment seg, boolean complexOrRepeated) { |
| while (seg != null) { |
| if (seg.isArray() && !seg.isLastPath()) { |
| return true; |
| } |
| |
| if (seg.isLastPath() && complexOrRepeated) { |
| return true; |
| } |
| |
| seg = seg.getChild(); |
| } |
| return false; |
| } |
| |
| /** |
| * Adds code to {@code eval} block which reads values by key from {@code expr} which is an instance of |
| * {@link org.apache.drill.exec.vector.complex.reader.BaseReader.DictReader}. |
| * |
| * |
| * @param generator current class generator |
| * @param eval evaluation block the code will be added to |
| * @param expr DICT reader to read values from |
| * @param segment segment containing original key value |
| * @param valueIndex current value index (will be reassigned in the method) |
| * @param isNull variable to indicate whether entry with the key exists in the DICT. |
| * Will be set to {@literal 1} if the key is not present |
| * @return expression corresponding to {@link org.apache.drill.exec.vector.complex.DictVector#FIELD_VALUE_NAME}'s |
| * reader with its position set to index corresponding to the key |
| */ |
| private JExpression getDictReaderReadByKeyExpression(ClassGenerator<?> generator, JBlock eval, JExpression expr, |
| PathSegment segment, JVar valueIndex, JVar isNull) { |
| JVar dictReader = generator.declareClassField("dictReader", generator.getModel()._ref(FieldReader.class)); |
| eval.assign(dictReader, expr); |
| |
| JExpression keyExpr = getKeyExpression(segment, generator); |
| |
| eval.assign(valueIndex, expr.invoke("find").arg(keyExpr)); |
| |
| JConditional conditional = eval._if(valueIndex.gt(JExpr.lit(-1))); |
| JBlock ifFound = conditional._then().block(); |
| expr = dictReader.invoke("reader").arg(JExpr.lit("value")); |
| ifFound.add(expr.invoke("setPosition").arg(valueIndex)); |
| |
| JBlock elseBlock = conditional._else().block(); |
| |
| JClass nrClass = generator.getModel().ref(org.apache.drill.exec.vector.complex.impl.NullReader.class); |
| JExpression nullReader = nrClass.staticRef("EMPTY_MAP_INSTANCE"); |
| |
| elseBlock.assign(dictReader, nullReader); |
| if (isNull != null) { |
| elseBlock.assign(isNull, JExpr.lit(1)); |
| } |
| |
| return expr; |
| } |
| |
| /** |
| * Transforms a segment to appropriate Java Object representation of key ({@link org.apache.drill.exec.vector.complex.DictVector#FIELD_KEY_NAME}) |
| * which is used when retrieving values from dict with key. In case if key vector's Java equivalent is primitive, |
| * i.e. {@code boolean}, {@code int}, {@code double} etc., then primitive type is used. |
| * Otherwise, an {@code Object} instance is created (i.e, {@code BigDecimal} for {@link org.apache.drill.common.types.TypeProtos.MinorType#VARDECIMAL}, |
| * {@code LocalDateTime} for {@link org.apache.drill.common.types.TypeProtos.MinorType#TIMESTAMP} etc.). |
| * |
| * @param segment a path segment providing the value |
| * @param generator current class generator |
| * @return Java Object representation of key wrapped into {@link JVar} |
| */ |
| private JExpression getKeyExpression(PathSegment segment, ClassGenerator<?> generator) { |
| MajorType valueType = segment.getOriginalValueType(); |
| JType keyType; |
| JExpression newKeyObject; |
| JVar dictKey; |
| if (segment.isArray()) { |
| if (valueType == null) { |
| return JExpr.lit(segment.getArraySegment().getIndex()); |
| } |
| switch(valueType.getMinorType()) { |
| case INT: |
| return JExpr.cast(generator.getModel().ref(Object.class), JExpr.lit(segment.getArraySegment().getIndex())); |
| case SMALLINT: |
| return JExpr.lit((short) segment.getOriginalValue()); |
| case TINYINT: |
| return JExpr.lit((byte) segment.getOriginalValue()); |
| default: |
| throw new IllegalArgumentException("ArraySegment!"); |
| } |
| } else { // named |
| if (valueType == null) { |
| return JExpr.lit(segment.getNameSegment().getPath()); |
| } |
| switch (valueType.getMinorType()) { |
| case VARCHAR: |
| String vcValue = (String) segment.getOriginalValue(); |
| keyType = generator.getModel()._ref(org.apache.drill.exec.util.Text.class); |
| newKeyObject = JExpr._new(keyType).arg(vcValue); |
| dictKey = generator.declareClassField("dictKey", keyType); |
| generator.getSetupBlock().assign(dictKey, newKeyObject); |
| return dictKey; |
| case VARDECIMAL: |
| BigDecimal bdValue = (BigDecimal) segment.getOriginalValue(); |
| keyType = generator.getModel()._ref(java.math.BigDecimal.class); |
| |
| JClass rmClass = generator.getModel().ref(java.math.RoundingMode.class); |
| newKeyObject = JExpr._new(keyType).arg(JExpr.lit(bdValue.doubleValue())).invoke("setScale") |
| .arg(JExpr.lit(bdValue.scale())) |
| .arg(rmClass.staticRef("HALF_UP")); |
| dictKey = generator.declareClassField("dictKey", keyType); |
| generator.getSetupBlock().assign(dictKey, newKeyObject); |
| return dictKey; |
| case BIGINT: |
| return JExpr.lit((long) segment.getOriginalValue()); |
| case FLOAT4: |
| return JExpr.lit((float) segment.getOriginalValue()); |
| case FLOAT8: |
| return JExpr.lit((double) segment.getOriginalValue()); |
| case BIT: |
| return JExpr.lit((boolean) segment.getOriginalValue()); |
| case TIMESTAMP: |
| return getDateTimeKey(segment, generator, LocalDateTime.class, "parseBest"); |
| case DATE: |
| return getDateTimeKey(segment, generator, LocalDate.class, "parseLocalDate"); |
| case TIME: |
| return getDateTimeKey(segment, generator, LocalTime.class, "parseLocalTime"); |
| default: |
| throw new IllegalArgumentException("NamedSegment!"); |
| } |
| } |
| } |
| |
| private JVar getDateTimeKey(PathSegment segment, ClassGenerator<?> generator, Class<?> javaClass, String methodName) { |
| String strValue = segment.getNameSegment().getPath(); |
| |
| JClass dateUtilityClass = generator.getModel().ref(org.apache.drill.exec.expr.fn.impl.DateUtility.class); |
| JExpression newKeyObject = dateUtilityClass.staticInvoke(methodName).arg(JExpr.lit(strValue)); |
| JType keyType = generator.getModel()._ref(javaClass); |
| JVar dictKey = generator.declareClassField("dictKey", keyType); |
| generator.getSetupBlock().assign(dictKey, newKeyObject); |
| return dictKey; |
| } |
| |
| private HoldingContainer visitReturnValueExpression(ReturnValueExpression e, ClassGenerator<?> generator) { |
| LogicalExpression child = e.getChild(); |
| // Preconditions.checkArgument(child.getMajorType().equals(Types.REQUIRED_BOOLEAN)); |
| HoldingContainer hc = child.accept(this, generator); |
| if (e.isReturnTrueOnOne()) { |
| generator.getEvalBlock()._return(hc.getValue().eq(JExpr.lit(1))); |
| } else { |
| generator.getEvalBlock()._return(hc.getValue()); |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public HoldingContainer visitQuotedStringConstant(QuotedString e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getVarCharHolder(buffer, e.getString())); |
| } |
| |
| @Override |
| public HoldingContainer visitIntervalDayConstant(IntervalDayExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getIntervalDayHolder(e.getIntervalDay(), e.getIntervalMillis())); |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal9Constant(Decimal9Expression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getDecimal9Holder(e.getIntFromDecimal(), e.getPrecision(), e.getScale())); |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal18Constant(Decimal18Expression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getDecimal18Holder(e.getLongFromDecimal(), e.getPrecision(), e.getScale())); |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal28Constant(Decimal28Expression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getDecimal28Holder(buffer, e.getBigDecimal())); |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal38Constant(Decimal38Expression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getDecimal38Holder(buffer, e.getBigDecimal())); |
| } |
| |
| @Override |
| public HoldingContainer visitVarDecimalConstant(VarDecimalExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return getHoldingContainer( |
| generator, |
| e.getMajorType(), |
| buffer -> ValueHolderHelper.getVarDecimalHolder(buffer, e.getBigDecimal())); |
| } |
| |
| @Override |
| public HoldingContainer visitCastExpression(CastExpression e, ClassGenerator<?> value) throws RuntimeException { |
| throw new UnsupportedOperationException("CastExpression is not expected here. " |
| + "It should have been converted to FunctionHolderExpression in materialization"); |
| } |
| |
| @Override |
| public HoldingContainer visitConvertExpression(ConvertExpression e, ClassGenerator<?> value) |
| throws RuntimeException { |
| String convertFunctionName = e.getConvertFunction() + e.getEncodingType(); |
| |
| List<LogicalExpression> newArgs = new ArrayList<>(); |
| newArgs.add(e.getInput()); // input_expr |
| |
| FunctionCall fc = new FunctionCall(convertFunctionName, newArgs, e.getPosition()); |
| return fc.accept(this, value); |
| } |
| |
| @Override |
| public HoldingContainer visitAnyValueExpression(AnyValueExpression e, ClassGenerator<?> value) |
| throws RuntimeException { |
| |
| List<LogicalExpression> newArgs = new ArrayList<>(); |
| newArgs.add(e.getInput()); // input_expr |
| |
| FunctionCall fc = new FunctionCall(AnyValueExpression.ANY_VALUE, newArgs, e.getPosition()); |
| return fc.accept(this, value); |
| } |
| |
| private HoldingContainer visitBooleanAnd(BooleanOperator op, |
| ClassGenerator<?> generator) { |
| |
| HoldingContainer out = generator.declare(op.getMajorType()); |
| |
| JLabel label = generator.getEvalBlockLabel("AndOP"); |
| JBlock eval = generator.createInnerEvalBlock(); |
| generator.nestEvalBlock(eval); // enter into nested block |
| |
| HoldingContainer arg = null; |
| |
| JExpression e = null; |
| |
| // 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 |
| for (LogicalExpression expr : op.args()) { |
| arg = expr.accept(this, generator); |
| |
| JBlock earlyExit = null; |
| if (arg.isOptional()) { |
| JExpression expr1 = arg.getIsSet().eq(JExpr.lit(1)); |
| // UntypedNullHolder does not have `value` field |
| if (arg.getMinorType() != TypeProtos.MinorType.NULL) { |
| expr1 = expr1.cand(arg.getValue().ne(JExpr.lit(1))); |
| } |
| earlyExit = eval._if(expr1)._then(); |
| if (e == null) { |
| e = arg.getIsSet(); |
| } else { |
| e = e.mul(arg.getIsSet()); |
| } |
| } else { |
| earlyExit = eval._if(arg.getValue().ne(JExpr.lit(1)))._then(); |
| } |
| |
| if (out.isOptional()) { |
| earlyExit.assign(out.getIsSet(), JExpr.lit(1)); |
| } |
| |
| earlyExit.assign(out.getValue(), JExpr.lit(0)); |
| earlyExit._break(label); |
| } |
| |
| if (out.isOptional()) { |
| assert (e != null); |
| |
| JConditional notSetJC = eval._if(e.eq(JExpr.lit(0))); |
| notSetJC._then().assign(out.getIsSet(), JExpr.lit(0)); |
| |
| JBlock setBlock = notSetJC._else().block(); |
| setBlock.assign(out.getIsSet(), JExpr.lit(1)); |
| setBlock.assign(out.getValue(), JExpr.lit(1)); |
| } else { |
| assert (e == null); |
| eval.assign(out.getValue(), JExpr.lit(1)); |
| } |
| |
| generator.unNestEvalBlock(); // exit from nested block |
| |
| return out; |
| } |
| |
| private HoldingContainer visitBooleanOr(BooleanOperator op, |
| ClassGenerator<?> generator) { |
| |
| HoldingContainer out = generator.declare(op.getMajorType()); |
| |
| JLabel label = generator.getEvalBlockLabel("OrOP"); |
| JBlock eval = generator.createInnerEvalBlock(); |
| generator.nestEvalBlock(eval); // enter into nested block. |
| |
| HoldingContainer arg = null; |
| |
| JExpression e = null; |
| |
| // 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 |
| for (LogicalExpression expr : op.args()) { |
| arg = expr.accept(this, generator); |
| |
| JBlock earlyExit = null; |
| if (arg.isOptional()) { |
| earlyExit = eval._if(arg.getIsSet().eq(JExpr.lit(1)).cand(arg.getValue().eq(JExpr.lit(1))))._then(); |
| if (e == null) { |
| e = arg.getIsSet(); |
| } else { |
| e = e.mul(arg.getIsSet()); |
| } |
| } else { |
| earlyExit = eval._if(arg.getValue().eq(JExpr.lit(1)))._then(); |
| } |
| |
| if (out.isOptional()) { |
| earlyExit.assign(out.getIsSet(), JExpr.lit(1)); |
| } |
| |
| earlyExit.assign(out.getValue(), JExpr.lit(1)); |
| earlyExit._break(label); |
| } |
| |
| if (out.isOptional()) { |
| assert (e != null); |
| |
| JConditional notSetJC = eval._if(e.eq(JExpr.lit(0))); |
| notSetJC._then().assign(out.getIsSet(), JExpr.lit(0)); |
| |
| JBlock setBlock = notSetJC._else().block(); |
| setBlock.assign(out.getIsSet(), JExpr.lit(1)); |
| setBlock.assign(out.getValue(), JExpr.lit(0)); |
| } else { |
| assert (e == null); |
| eval.assign(out.getValue(), JExpr.lit(0)); |
| } |
| |
| generator.unNestEvalBlock(); // exit from nested block. |
| |
| return out; |
| } |
| } |
| |
| /** |
| * Creates a representation of the "Holder" for a constant |
| * value. For optimization, constant holders are cached. This |
| * visitor retrieves an existing cached holder, if available, else |
| * creates a new one. |
| */ |
| private class CSEFilter extends ConstantFilter { |
| |
| public CSEFilter(Set<LogicalExpression> constantBoundaries) { |
| super(constantBoundaries); |
| } |
| |
| @Override |
| public HoldingContainer visitFunctionCall(FunctionCall call, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(call, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitFunctionCall(call, generator); |
| put(call, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitFunctionHolderExpression(FunctionHolderExpression holder, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(holder, generator.getMappingSet()); |
| if (hc == null || holder.isRandom()) { |
| hc = super.visitFunctionHolderExpression(holder, generator); |
| put(holder, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitIfExpression(IfExpression ifExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(ifExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitIfExpression(ifExpr, generator); |
| put(ifExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitBooleanOperator(BooleanOperator call, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(call, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitBooleanOperator(call, generator); |
| put(call, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitSchemaPath(SchemaPath path, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(path, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitSchemaPath(path, generator); |
| put(path, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitIntConstant(IntExpression intExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(intExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitIntConstant(intExpr, generator); |
| put(intExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitFloatConstant(FloatExpression fExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(fExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitFloatConstant(fExpr, generator); |
| put(fExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitLongConstant(LongExpression longExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(longExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitLongConstant(longExpr, generator); |
| put(longExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitDateConstant(DateExpression dateExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(dateExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitDateConstant(dateExpr, generator); |
| put(dateExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitTimeConstant(TimeExpression timeExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(timeExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitTimeConstant(timeExpr, generator); |
| put(timeExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitTimeStampConstant(TimeStampExpression timeStampExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(timeStampExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitTimeStampConstant(timeStampExpr, generator); |
| put(timeStampExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitIntervalYearConstant(IntervalYearExpression intervalYearExpression, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(intervalYearExpression, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitIntervalYearConstant(intervalYearExpression, generator); |
| put(intervalYearExpression, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitIntervalDayConstant(IntervalDayExpression intervalDayExpression, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(intervalDayExpression, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitIntervalDayConstant(intervalDayExpression, generator); |
| put(intervalDayExpression, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal9Constant(Decimal9Expression decExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(decExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitDecimal9Constant(decExpr, generator); |
| put(decExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal18Constant(Decimal18Expression decExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(decExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitDecimal18Constant(decExpr, generator); |
| put(decExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal28Constant(Decimal28Expression decExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(decExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitDecimal28Constant(decExpr, generator); |
| put(decExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal38Constant(Decimal38Expression decExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(decExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitDecimal38Constant(decExpr, generator); |
| put(decExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitVarDecimalConstant(VarDecimalExpression decExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(decExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitVarDecimalConstant(decExpr, generator); |
| put(decExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitDoubleConstant(DoubleExpression dExpr, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(dExpr, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitDoubleConstant(dExpr, generator); |
| put(dExpr, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitBooleanConstant(BooleanExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(e, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitBooleanConstant(e, generator); |
| put(e, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitQuotedStringConstant(QuotedString e, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(e, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitQuotedStringConstant(e, generator); |
| put(e, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitNullConstant(TypedNullConstant e, ClassGenerator<?> generator) throws RuntimeException { |
| return super.visitNullConstant(e, generator); |
| } |
| |
| @Override |
| public HoldingContainer visitNullExpression(NullExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(e, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitNullExpression(e, generator); |
| put(e, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitUnknown(LogicalExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| if (e instanceof ValueVectorReadExpression) { |
| HoldingContainer hc = getPrevious(e, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitUnknown(e, generator); |
| put(e, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| return super.visitUnknown(e, generator); |
| } |
| |
| @Override |
| public HoldingContainer visitCastExpression(CastExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(e, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitCastExpression(e, generator); |
| put(e, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| |
| @Override |
| public HoldingContainer visitConvertExpression(ConvertExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| HoldingContainer hc = getPrevious(e, generator.getMappingSet()); |
| if (hc == null) { |
| hc = super.visitConvertExpression(e, generator); |
| put(e, hc, generator.getMappingSet()); |
| } |
| return hc; |
| } |
| } |
| |
| /** |
| * An evaluation visitor which special cases constant (sub-) expressions |
| * of any form, passing non-constant expressions to the parent |
| * visitor. |
| */ |
| private static class ConstantFilter extends EvalVisitor { |
| |
| private final Set<LogicalExpression> constantBoundaries; |
| |
| public ConstantFilter(Set<LogicalExpression> constantBoundaries) { |
| this.constantBoundaries = constantBoundaries; |
| } |
| |
| @Override |
| public HoldingContainer visitFunctionCall(FunctionCall e, ClassGenerator<?> generator) throws RuntimeException { |
| throw new UnsupportedOperationException("FunctionCall is not expected here. " |
| + "It should have been converted to FunctionHolderExpression in materialization"); |
| } |
| |
| private HoldingContainer visitExpression(LogicalExpression e, ClassGenerator<?> generator, |
| VisitorCallback visitorCallback) throws RuntimeException { |
| if (constantBoundaries.contains(e)) { |
| generator.getMappingSet().enterConstant(); |
| HoldingContainer c = visitorCallback.getHolder(); |
| |
| return renderConstantExpression(generator, c, e); |
| } else if (generator.getMappingSet().isWithinConstant()) { |
| return visitorCallback.getHolder().setConstant(true); |
| } else { |
| return visitorCallback.getHolder(); |
| } |
| } |
| |
| @Override |
| public HoldingContainer visitFunctionHolderExpression(FunctionHolderExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitFunctionHolderExpression(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitBooleanOperator(BooleanOperator e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitBooleanOperator(e, generator)); |
| } |
| @Override |
| public HoldingContainer visitIfExpression(IfExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitIfExpression(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitSchemaPath(SchemaPath e, ClassGenerator<?> generator) throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitSchemaPath(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitLongConstant(LongExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitLongConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal9Constant(Decimal9Expression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitDecimal9Constant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal18Constant(Decimal18Expression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitDecimal18Constant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal28Constant(Decimal28Expression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitDecimal28Constant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitDecimal38Constant(Decimal38Expression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitDecimal38Constant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitVarDecimalConstant(VarDecimalExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitVarDecimalConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitIntConstant(IntExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitIntConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitDateConstant(DateExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitDateConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitTimeConstant(TimeExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitTimeConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitIntervalYearConstant(IntervalYearExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitIntervalYearConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitTimeStampConstant(TimeStampExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitTimeStampConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitFloatConstant(FloatExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitFloatConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitDoubleConstant(DoubleExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitDoubleConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitBooleanConstant(BooleanExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitBooleanConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitUnknown(LogicalExpression e, ClassGenerator<?> generator) throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitUnknown(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitQuotedStringConstant(QuotedString e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitQuotedStringConstant(e, generator)); |
| } |
| |
| @Override |
| public HoldingContainer visitIntervalDayConstant(IntervalDayExpression e, ClassGenerator<?> generator) |
| throws RuntimeException { |
| return visitExpression(e, generator, () -> super.visitIntervalDayConstant(e, generator)); |
| } |
| |
| /** |
| * Get a HoldingContainer for a constant expression. The returned |
| * HoldingContainer will indicate it's for a constant expression. |
| */ |
| private HoldingContainer renderConstantExpression(ClassGenerator<?> generator, HoldingContainer input, |
| LogicalExpression expr) { |
| MajorType.Builder newTypeBuilder = MajorType.newBuilder(input.getMajorType()); |
| |
| if (expr instanceof DrillFuncHolderExpr && |
| ((DrillFuncHolderExpr) expr).getHolder().getNullHandling() == FunctionTemplate.NullHandling.NULL_IF_NULL) { |
| newTypeBuilder.setMode(expr.getMajorType().getMode()); |
| } |
| MajorType newType = newTypeBuilder.build(); |
| JVar fieldValue = generator.declareClassField("constant", generator.getHolderType(newType)); |
| |
| // Creates a new vector for class field and assigns to its fields values from output field |
| // to allow scalar replacement for source objects |
| generator.getEvalBlock().assign(fieldValue, JExpr._new(generator.getHolderType(newType))); |
| List<String> holderFields = ValueHolderHelper.getHolderParams(newType); |
| for (String holderField : holderFields) { |
| generator.getEvalBlock().assign(fieldValue.ref(holderField), input.getHolder().ref(holderField)); |
| } |
| generator.getMappingSet().exitConstant(); |
| return new HoldingContainer(input.getMajorType(), fieldValue, fieldValue.ref("value"), fieldValue.ref("isSet")) |
| .setConstant(true); |
| } |
| } |
| } |