diff options
author | Jinfeng Ni <jni@maprtech.com> | 2014-05-30 11:07:03 -0700 |
---|---|---|
committer | Jacques Nadeau <jacques@apache.org> | 2014-06-10 18:59:28 -0700 |
commit | 31b5962250675c45bcbe3c3fe6babeba5deb21fc (patch) | |
tree | 831067db75422132682c11a2b764d71396249a5c | |
parent | 1226af8bf7b131097c1c6c724d67d126a344570d (diff) |
DRILL-927: Run-time code generation support for reading Complex Type.
Fix in RepeatedMapVector.
24 files changed, 1092 insertions, 210 deletions
diff --git a/common/src/main/java/org/apache/drill/common/types/Types.java b/common/src/main/java/org/apache/drill/common/types/Types.java index e2c331056..10d84e1d1 100644 --- a/common/src/main/java/org/apache/drill/common/types/Types.java +++ b/common/src/main/java/org/apache/drill/common/types/Types.java @@ -46,6 +46,10 @@ public class Types { return false; } + public static boolean isRepeated(MajorType type){ + return type.getMode() == REPEATED ; + } + public static boolean isNumericType(MajorType type){ if(type.getMode() == REPEATED) return false; @@ -344,6 +348,8 @@ public class Types { return withMode(MinorType.TIME, mode); case "binary": return withMode(MinorType.VARBINARY, mode); + case "json": + return withMode(MinorType.VARBINARY, mode); default: throw new UnsupportedOperationException("Could not determine type: " + typeName); } diff --git a/exec/java-exec/src/main/codegen/templates/BaseReader.java b/exec/java-exec/src/main/codegen/templates/BaseReader.java index 42f482694..d432b7445 100644 --- a/exec/java-exec/src/main/codegen/templates/BaseReader.java +++ b/exec/java-exec/src/main/codegen/templates/BaseReader.java @@ -39,6 +39,7 @@ public interface BaseReader extends Positionable{ public interface RepeatedMapReader extends MapReader{ boolean next(); int size(); + void copyAsValue(MapWriter writer); } public interface ListReader extends BaseReader{ diff --git a/exec/java-exec/src/main/codegen/templates/NullReader.java b/exec/java-exec/src/main/codegen/templates/NullReader.java index 962eeda0b..beec5cb3d 100644 --- a/exec/java-exec/src/main/codegen/templates/NullReader.java +++ b/exec/java-exec/src/main/codegen/templates/NullReader.java @@ -34,6 +34,13 @@ public class NullReader extends AbstractBaseReader implements FieldReader{ private NullReader(){ super(); } + + @Override + public MajorType getType() { + return Types.NULL; + } + + public void copyAsValue(MapWriter writer) {} <#list vv.types as type><#list type.minor as minor><#assign name = minor.class?cap_first /> public void read(Nullable${name}Holder holder){ diff --git a/exec/java-exec/src/main/codegen/templates/RepeatedValueVectors.java b/exec/java-exec/src/main/codegen/templates/RepeatedValueVectors.java index 657011340..48efc163c 100644 --- a/exec/java-exec/src/main/codegen/templates/RepeatedValueVectors.java +++ b/exec/java-exec/src/main/codegen/templates/RepeatedValueVectors.java @@ -130,9 +130,6 @@ package org.apache.drill.exec.vector; } } - - -<#if type.major == "VarLen"> public void copyFrom(int inIndex, int outIndex, Repeated${minor.class}Vector v){ int count = v.getAccessor().getCount(inIndex); getMutator().startNewGroup(outIndex); @@ -151,16 +148,6 @@ package org.apache.drill.exec.vector; } return true; } -<#else> - - public void copyFrom(int inIndex, int outIndex, Repeated${minor.class}Vector v){ - throw new UnsupportedOperationException(); - } - - public boolean copyFromSafe(int inIndex, int outIndex, Repeated${minor.class}Vector v){ - throw new UnsupportedOperationException(); - } -</#if> public boolean allocateNewSafe(){ if(!offsets.allocateNewSafe()) return false; @@ -341,12 +328,17 @@ package org.apache.drill.exec.vector; public void get(int index, int positionIndex, ${minor.class}Holder holder) { int offset = offsets.getAccessor().get(index); assert offset >= 0; + assert positionIndex < getCount(index); values.getAccessor().get(offset + positionIndex, holder); } public void get(int index, int positionIndex, Nullable${minor.class}Holder holder) { int offset = offsets.getAccessor().get(index); assert offset >= 0; + if (positionIndex >= getCount(index)) { + holder.isSet = 0; + return; + } values.getAccessor().get(offset + positionIndex, holder); } @@ -407,9 +399,19 @@ package org.apache.drill.exec.vector; return (b1 && b2); } - + <#else> + + public boolean addSafe(int index, ${minor.javaType!type.javaType} srcValue) { + if(offsets.getValueCapacity() <= index+1) return false; + int nextOffset = offsets.getAccessor().get(index+1); + boolean b1 = values.getMutator().setSafe(nextOffset, srcValue); + boolean b2 = offsets.getMutator().setSafe(index+1, nextOffset+1); + return (b1 && b2); + } + </#if> + public boolean setSafe(int index, Repeated${minor.class}Holder h){ ${minor.class}Holder ih = new ${minor.class}Holder(); getMutator().startNewGroup(index); diff --git a/exec/java-exec/src/main/codegen/templates/SingularReaderImpl.java b/exec/java-exec/src/main/codegen/templates/SingularReaderImpl.java new file mode 100644 index 000000000..33cedba3c --- /dev/null +++ b/exec/java-exec/src/main/codegen/templates/SingularReaderImpl.java @@ -0,0 +1,210 @@ +/** + * 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. + */ + +<@pp.dropOutputFile /> +<#list vv.types as type> +<#list type.minor as minor> +<#list ["", "Nullable"] as nullMode> +<#assign lowerName = minor.class?uncap_first /> +<#if lowerName == "int" ><#assign lowerName = "integer" /></#if> +<#assign name = minor.class?cap_first /> +<#assign javaType = (minor.javaType!type.javaType) /> +<#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) /> +<#assign safeType=friendlyType /> +<#if safeType=="byte[]"><#assign safeType="ByteArray" /></#if> + +<@pp.changeOutputFile name="/org/apache/drill/exec/vector/complex/impl/${nullMode}${name}SingularReaderImpl.java" /> +<#include "/@includes/license.ftl" /> + +package org.apache.drill.exec.vector.complex.impl; + +<#include "/@includes/vv_imports.ftl" /> + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.apache.drill.exec.expr.holders.*; +import org.apache.hadoop.io.Text; +import org.joda.time.Period; +import org.mortbay.jetty.servlet.Holder; + +@SuppressWarnings("unused") +public class ${nullMode}${name}SingularReaderImpl extends AbstractFieldReader { + + private ${nullMode}${name}Holder holder; + + public ${nullMode}${name}SingularReaderImpl(${nullMode}${name}Holder holder) { + this.holder = holder; + } + + @Override + public int size() { + throw new UnsupportedOperationException("You can't call size on a singular value reader."); + } + + @Override + public boolean next() { + throw new UnsupportedOperationException("You can't call next on a single value reader."); + } + + @Override + public void setPosition(int index) { + throw new UnsupportedOperationException("You can't call next on a single value reader."); + } + + @Override + public MajorType getType() { + return this.holder.getType(); + } + + @Override + public boolean isSet() { + return this.holder.isSet(); + } + + @Override + public ${friendlyType} read${safeType}(){ + <#if nullMode == "Nullable"> + + if (!holder.isSet()) { + return null; + } + </#if> + + <#if type.major == "VarLen"> + + int length = holder.end - holder.start; + byte[] value = new byte [length]; + holder.buffer.getBytes(holder.start, value, 0, length); + + <#if minor.class == "VarBinary"> + return value; + <#elseif minor.class == "Var16Char"> + return new String(value); + <#elseif minor.class == "VarChar"> + Text text = new Text(); + text.set(value); + return text; + </#if> + + <#elseif minor.class == "Interval"> + Period p = new Period(); + return p.plusMonths(holder.months).plusDays(holder.days).plusMillis(holder.milliSeconds); + + <#elseif minor.class == "IntervalDay"> + Period p = new Period(); + return p.plusDays(holder.days).plusMillis(holder.milliSeconds); + + <#elseif minor.class == "Decimal9" || + minor.class == "Decimal18" > + BigInteger value = BigInteger.valueOf(holder.value); + return new BigDecimal(value, holder.scale); + + <#elseif minor.class == "Decimal28Dense" || + minor.class == "Decimal38Dense"> + return org.apache.drill.common.util.DecimalUtility.getBigDecimalFromDense(holder.buffer, + holder.start, + holder.nDecimalDigits, + holder.scale, + holder.maxPrecision, + holder.WIDTH); + + <#elseif minor.class == "Decimal28Sparse" || + minor.class == "Decimal38Sparse"> + return org.apache.drill.common.util.DecimalUtility.getBigDecimalFromSparse(holder.buffer, + holder.start, + holder.nDecimalDigits, + holder.scale); + + <#elseif minor.class == "Bit" > + return new Boolean(holder.value != 0); + <#else> + ${friendlyType} value = new ${friendlyType}(this.holder.value); + return value; + </#if> + + } + + @Override + public Object readObject(){ + + <#if nullMode == "Nullable"> + + if (!holder.isSet()) { + return null; + } + </#if> + + <#if type.major == "VarLen"> + + int length = holder.end - holder.start; + byte[] value = new byte [length]; + holder.buffer.getBytes(holder.start, value, 0, length); + + <#if minor.class == "VarBinary"> + return value; + <#elseif minor.class == "Var16Char"> + return new String(value); + <#elseif minor.class == "VarChar"> + Text text = new Text(); + text.set(value); + return text; + </#if> + + <#elseif minor.class == "Interval"> + Period p = new Period(); + return p.plusMonths(holder.months).plusDays(holder.days).plusMillis(holder.milliSeconds); + + <#elseif minor.class == "IntervalDay"> + Period p = new Period(); + return p.plusDays(holder.days).plusMillis(holder.milliSeconds); + + <#elseif minor.class == "Decimal9" || + minor.class == "Decimal18" > + BigInteger value = BigInteger.valueOf(holder.value); + return new BigDecimal(value, holder.scale); + + <#elseif minor.class == "Decimal28Dense" || + minor.class == "Decimal38Dense"> + return org.apache.drill.common.util.DecimalUtility.getBigDecimalFromDense(holder.buffer, + holder.start, + holder.nDecimalDigits, + holder.scale, + holder.maxPrecision, + holder.WIDTH); + + <#elseif minor.class == "Decimal28Sparse" || + minor.class == "Decimal38Sparse"> + return org.apache.drill.common.util.DecimalUtility.getBigDecimalFromSparse(holder.buffer, + holder.start, + holder.nDecimalDigits, + holder.scale); + + <#elseif minor.class == "Bit" > + return new Boolean(holder.value != 0); + <#else> + ${friendlyType} value = new ${friendlyType}(this.holder.value); + return value; + </#if> + } + +} + +</#list> +</#list> +</#list> diff --git a/exec/java-exec/src/main/codegen/templates/TypeHelper.java b/exec/java-exec/src/main/codegen/templates/TypeHelper.java index 5f24a3087..8c56d99ef 100644 --- a/exec/java-exec/src/main/codegen/templates/TypeHelper.java +++ b/exec/java-exec/src/main/codegen/templates/TypeHelper.java @@ -115,14 +115,17 @@ public class TypeHelper { } throw new UnsupportedOperationException(); } - public static Class<?> getReaderClassName( MinorType type, DataMode mode){ + public static Class<?> getReaderClassName( MinorType type, DataMode mode, boolean isSingularRepeated){ switch (type) { case MAP: switch (mode) { case REQUIRED: - return SingleMapReaderImpl.class; - case REPEATED: - return RepeatedMapReaderImpl.class; + if (!isSingularRepeated) + return SingleMapReaderImpl.class; + else + return SingleLikeRepeatedMapReaderImpl.class; + case REPEATED: + return RepeatedMapReaderImpl.class; } case LIST: switch (mode) { @@ -201,6 +204,25 @@ public class TypeHelper { } throw new UnsupportedOperationException(); } + + public static Class<?> getSingularReaderImpl( MinorType type, DataMode mode){ + switch (type) { +<#list vv.types as type> + <#list type.minor as minor> + case ${minor.class?upper_case}: + switch (mode) { + case REQUIRED: + return ${minor.class}SingularReaderImpl.class; + case OPTIONAL: + return Nullable${minor.class}SingularReaderImpl.class; + } + </#list> +</#list> + default: + break; + } + throw new UnsupportedOperationException(); + } public static JType getHolderType(JCodeModel model, MinorType type, DataMode mode){ switch (type) { diff --git a/exec/java-exec/src/main/codegen/templates/ValueHolders.java b/exec/java-exec/src/main/codegen/templates/ValueHolders.java index 5cea7e312..cdc4d74b2 100644 --- a/exec/java-exec/src/main/codegen/templates/ValueHolders.java +++ b/exec/java-exec/src/main/codegen/templates/ValueHolders.java @@ -32,14 +32,28 @@ package org.apache.drill.exec.expr.holders; public final class ${className} implements ValueHolder{ public static final MajorType TYPE = Types.${mode.name?lower_case}(MinorType.${minor.class?upper_case}); - + + public MajorType getType() {return TYPE;} + <#if mode.name != "Repeated"> public static final int WIDTH = ${type.width}; <#if mode.name == "Optional"> /** Whether the given holder holds a valid value. 1 means non-null. 0 means null. **/ public int isSet; + + public boolean isSet() { + return isSet == 1; + } + <#else> + + public boolean isSet() { + return true; + } + </#if> + + <#if type.major != "VarLen"> @@ -132,6 +146,11 @@ public final class ${className} implements ValueHolder{ /** The Vector holding the actual values. **/ public ${minor.class}Vector vector; + + public boolean isSet() { + return true; + } + </#if> } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/EvaluationVisitor.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/EvaluationVisitor.java index 1c012ab61..31cc56307 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/EvaluationVisitor.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/EvaluationVisitor.java @@ -20,6 +20,7 @@ package org.apache.drill.exec.expr; import java.util.List; import java.util.Set; +import org.apache.commons.io.input.NullReader; import org.apache.drill.common.expression.CastExpression; import org.apache.drill.common.expression.ConvertExpression; import org.apache.drill.common.expression.FunctionCall; @@ -46,6 +47,7 @@ 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.visitors.AbstractExprVisitor; +import org.apache.drill.common.types.TypeProtos.DataMode; import org.apache.drill.common.types.TypeProtos.MajorType; import org.apache.drill.common.types.TypeProtos.MinorType; import org.apache.drill.common.types.Types; @@ -80,33 +82,32 @@ public class EvaluationVisitor { this.registry = registry; } - public HoldingContainer addExpr(LogicalExpression e, ClassGenerator<?> generator){ + public HoldingContainer addExpr(LogicalExpression e, ClassGenerator<?> generator) { Set<LogicalExpression> constantBoundaries = ConstantExpressionIdentifier.getConstantExpressionSet(e); - //Set<LogicalExpression> constantBoundaries = Collections.emptySet(); return e.accept(new ConstantFilter(constantBoundaries), generator); } private 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"); + throw new UnsupportedOperationException("FunctionCall is not expected here. " + + "It should have been converted to FunctionHolderExpression in materialization"); } @Override - public HoldingContainer visitFunctionHolderExpression( - FunctionHolderExpression holderExpr, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitFunctionHolderExpression(FunctionHolderExpression holderExpr, + ClassGenerator<?> generator) throws RuntimeException { // TODO: hack: (Drill/Hive)FuncHolderExpr reference classes in exec so // code generate methods can't be superclass FunctionHolderExpression // which is defined in common if (holderExpr instanceof DrillFuncHolderExpr) { - DrillFuncHolder holder = ((DrillFuncHolderExpr)holderExpr).getHolder(); + DrillFuncHolder holder = ((DrillFuncHolderExpr) holderExpr).getHolder(); JVar[] workspaceVars = holder.renderStart(generator, null); - if(holder.isNested()) generator.getMappingSet().enterChild(); + if (holder.isNested()) + generator.getMappingSet().enterChild(); HoldingContainer[] args = new HoldingContainer[holderExpr.args.size()]; for (int i = 0; i < holderExpr.args.size(); i++) { @@ -115,13 +116,14 @@ public class EvaluationVisitor { holder.renderMiddle(generator, args, workspaceVars); - if(holder.isNested()) generator.getMappingSet().exitChild(); + if (holder.isNested()) + generator.getMappingSet().exitChild(); return holder.renderEnd(generator, args, workspaceVars); } else if (holderExpr instanceof HiveFuncHolderExpr) { - HiveFuncHolder holder = ((HiveFuncHolderExpr)holderExpr).getHolder(); + HiveFuncHolder holder = ((HiveFuncHolderExpr) holderExpr).getHolder(); HoldingContainer[] args = new HoldingContainer[holderExpr.args.size()]; for (int i = 0; i < holderExpr.args.size(); i++) { @@ -131,7 +133,8 @@ public class EvaluationVisitor { return holder.renderEnd(generator, args, holder.renderStart(generator, null)); } - throw new UnsupportedOperationException(String.format("Unknown expression '%s'", holderExpr.getClass().getCanonicalName())); + throw new UnsupportedOperationException(String.format("Unknown expression '%s'", holderExpr.getClass() + .getCanonicalName())); } @Override @@ -183,7 +186,6 @@ public class EvaluationVisitor { return output; } - @Override public HoldingContainer visitSchemaPath(SchemaPath path, ClassGenerator<?> generator) throws RuntimeException { throw new UnsupportedOperationException("All schema paths should have been replaced with ValueVectorExpressions."); @@ -218,21 +220,24 @@ public class EvaluationVisitor { } @Override - public HoldingContainer visitIntervalYearConstant(IntervalYearExpression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitIntervalYearConstant(IntervalYearExpression e, ClassGenerator<?> generator) + throws RuntimeException { HoldingContainer out = generator.declare(e.getMajorType()); generator.getEvalBlock().assign(out.getValue(), JExpr.lit(e.getIntervalYear())); return out; } @Override - public HoldingContainer visitTimeStampConstant(TimeStampExpression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitTimeStampConstant(TimeStampExpression e, ClassGenerator<?> generator) + throws RuntimeException { HoldingContainer out = generator.declare(e.getMajorType()); generator.getEvalBlock().assign(out.getValue(), JExpr.lit(e.getTimeStamp())); return out; } @Override - public HoldingContainer visitDoubleConstant(DoubleExpression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitDoubleConstant(DoubleExpression e, ClassGenerator<?> generator) + throws RuntimeException { HoldingContainer out = generator.declare(e.getMajorType()); generator.getEvalBlock().assign(out.getValue(), JExpr.lit(e.getDouble())); return out; @@ -254,13 +259,13 @@ public class EvaluationVisitor { return visitValueVectorWriteExpression((ValueVectorWriteExpression) e, generator); } else if (e instanceof ReturnValueExpression) { return visitReturnValueExpression((ReturnValueExpression) e, generator); - }else if(e instanceof HoldingContainerExpression){ + } else if (e instanceof HoldingContainerExpression) { return ((HoldingContainerExpression) e).getContainer(); - }else if(e instanceof NullExpression){ + } else if (e instanceof NullExpression) { return generator.declare(Types.optional(MinorType.INT)); } else if (e instanceof TypedNullConstant) { return generator.declare(e.getMajorType()); - } else { + } else { return super.visitUnknown(e, generator); } @@ -271,27 +276,30 @@ public class EvaluationVisitor { final LogicalExpression child = e.getChild(); final HoldingContainer inputContainer = child.accept(this, generator); final boolean complex = Types.isComplex(inputContainer.getMajorType()); + final boolean repeated = Types.isRepeated(inputContainer.getMajorType()); JBlock block = generator.getEvalBlock(); JExpression outIndex = generator.getMappingSet().getValueWriteIndex(); JVar vv = generator.declareVectorValueSetupAndMember(generator.getMappingSet().getOutgoing(), e.getFieldId()); - if(complex){ - JType writerImpl = generator.getModel()._ref(TypeHelper.getWriterImpl(inputContainer.getMinorType(), inputContainer.getMajorType().getMode())); - JType writerIFace = generator.getModel()._ref(TypeHelper.getWriterInterface(inputContainer.getMinorType(), inputContainer.getMajorType().getMode())); + if (complex || repeated) { + 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()){ + if (e.isSafe()) { HoldingContainer outputContainer = generator.declare(Types.REQUIRED_BIT); JConditional ifOut = generator.getEvalBlock()._if(writer.invoke("ok")); ifOut._then().assign(outputContainer.getValue(), JExpr.lit(1)); ifOut._else().assign(outputContainer.getValue(), JExpr.lit(0)); return outputContainer; } - }else{ + } else { String setMethod = e.isSafe() ? "setSafe" : "set"; String isSafeMethod = "isSafe"; @@ -299,24 +307,26 @@ public class EvaluationVisitor { JInvocation setMeth; if (Types.usesHolderForGet(inputContainer.getMajorType())) { setMeth = vv.invoke("getMutator").invoke(setMethod).arg(outIndex).arg(inputContainer.getHolder()); - }else{ + } else { setMeth = vv.invoke("getMutator").invoke(setMethod).arg(outIndex).arg(inputContainer.getValue()); } - if(e.isSafe()){ + if (e.isSafe()) { HoldingContainer outputContainer = generator.declare(Types.REQUIRED_BIT); block.assign(outputContainer.getValue(), JExpr.lit(1)); - if(inputContainer.isOptional()){ -// block._if(vv.invoke("getMutator").invoke(setMethod).arg(outIndex).not())._then().assign(outputContainer.getValue(), JExpr.lit(0)); + if (inputContainer.isOptional()) { + // block._if(vv.invoke("getMutator").invoke(setMethod).arg(outIndex).not())._then().assign(outputContainer.getValue(), + // JExpr.lit(0)); JConditional jc = block._if(inputContainer.getIsSet().eq(JExpr.lit(0)).not()); block = jc._then(); - jc._else()._if(vv.invoke("getMutator").invoke(isSafeMethod).arg(outIndex).not())._then().assign(outputContainer.getValue(), JExpr.lit(0)); + jc._else()._if(vv.invoke("getMutator").invoke(isSafeMethod).arg(outIndex).not())._then() + .assign(outputContainer.getValue(), JExpr.lit(0)); } block._if(setMeth.not())._then().assign(outputContainer.getValue(), JExpr.lit(0)); return outputContainer; - }else{ + } else { if (inputContainer.isOptional()) { -// block.add(vv.invoke("getMutator").invoke(setMethod).arg(outIndex)); + // block.add(vv.invoke("getMutator").invoke(setMethod).arg(outIndex)); JConditional jc = block._if(inputContainer.getIsSet().eq(JExpr.lit(0)).not()); block = jc._then(); } @@ -325,7 +335,6 @@ public class EvaluationVisitor { } - return null; } @@ -333,35 +342,37 @@ public class EvaluationVisitor { throws RuntimeException { // declare value vector - JExpression vv1 = generator.declareVectorValueSetupAndMember(generator.getMappingSet().getIncoming(), e.getFieldId()); + JExpression vv1 = generator.declareVectorValueSetupAndMember(generator.getMappingSet().getIncoming(), + e.getFieldId()); JExpression indexVariable = generator.getMappingSet().getValueReadIndex(); JExpression componentVariable = indexVariable.shrz(JExpr.lit(16)); if (e.isSuperReader()) { - vv1 = (vv1.component(componentVariable)); + vv1 = (vv1.component(componentVariable)); indexVariable = indexVariable.band(JExpr.lit((int) Character.MAX_VALUE)); } // evaluation work. HoldingContainer out = generator.declare(e.getMajorType()); + final boolean primitive = !Types.usesHolderForGet(e.getMajorType()); final boolean hasReadPath = e.hasReadPath(); final boolean complex = Types.isComplex(e.getMajorType()); + final boolean repeated = Types.isRepeated(e.getMajorType()); int[] fieldIds = e.getFieldId().getFieldIds(); - for(int i = 1; i < fieldIds.length; i++){ + for (int i = 1; i < fieldIds.length; i++) { } - if(!hasReadPath && !complex){ - + if (!hasReadPath && !complex) { JInvocation getValueAccessor = vv1.invoke("getAccessor").invoke("get"); JInvocation getValueAccessor2 = vv1.invoke("getAccessor"); JBlock eval = new JBlock(); - if(primitive){ + if (primitive) { eval.assign(out.getValue(), getValueAccessor.arg(indexVariable)); - }else{ + } else { eval.add(getValueAccessor.arg(indexVariable).arg(out.getHolder())); } @@ -370,13 +381,14 @@ public class EvaluationVisitor { blk.assign(out.getIsSet(), getValueAccessor2.invoke("isSet").arg(indexVariable)); JConditional jc = blk._if(out.getIsSet().eq(JExpr.lit(1))); jc._then().add(eval); - }else{ + } else { generator.getEvalBlock().add(eval); } - }else{ + } else { JExpression vector = e.isSuperReader() ? vv1.component(componentVariable) : vv1; JExpression expr = vector.invoke("getAccessor").invoke("getReader"); + JVar isNull = generator.getEvalBlock().decl(generator.getModel().INT, "isNull", JExpr.lit(0)); JLabel label = generator.getEvalBlock().label("complex"); JBlock eval = generator.getEvalBlock().block(); @@ -387,65 +399,77 @@ public class EvaluationVisitor { int listNum = 0; boolean lastWasArray = false; - while(seg != null){ - if(seg.isArray()){ + while (seg != null) { + if (seg.isArray()) { lastWasArray = true; - if(seg.isLastPath() && !complex) break; + if (seg.isLastPath() && !complex && !repeated) + break; JVar list = generator.declareClassField("list", generator.getModel()._ref(FieldReader.class)); - generator.getSetupBlock().assign(list, expr); + eval.assign(list, expr); expr = list; - // 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. + // 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(expr.invoke("next")) ).body().assign(currentIndex, currentIndex.plus(JExpr.lit(1))); + .cand(list.invoke("next"))).body().assign(currentIndex, currentIndex.plus(JExpr.lit(1))); + + expr = list.invoke("reader"); JBlock ifNoVal = eval._if(desiredIndex.ne(currentIndex))._then().block(); - if(!complex) ifNoVal.assign(out.getIsSet(), JExpr.lit(0)); + if (out.isOptional()) { + ifNoVal.assign(out.getIsSet(), JExpr.lit(0)); + } + ifNoVal.assign(isNull, JExpr.lit(1)); ifNoVal._break(label); listNum++; - - }else{ + } else { lastWasArray = false; JExpression fieldName = JExpr.lit(seg.getNameSegment().getPath()); expr = expr.invoke("reader").arg(fieldName); } seg = seg.getChild(); - // stop once we get to last column or when the segment is an array at the end of the reference. - if(seg == null || seg.isLastPath() && seg.isArray()) break; + // stop once we get to last column or when the segment is an array at + // the end of the reference. + // if(seg == null || seg.isLastPath() && seg.isArray()) break; } - MajorType secondaryType = e.getFieldId().getSecondaryFinal(); - JType readerImpl = generator.getModel()._ref(TypeHelper.getReaderClassName(secondaryType.getMinorType(), secondaryType.getMode())); - JVar complexReader = generator.declareClassField("reader", readerImpl); - generator.getSetupBlock().assign(complexReader, JExpr.cast(readerImpl, expr)); - expr = complexReader; - - if(complex){ - HoldingContainer hc = new HoldingContainer(e.getMajorType(), (JVar) expr, null, null, lastWasArray); + + if (complex || repeated) { + MajorType finalType = e.getFieldId().getFinalType(); + // // + JVar complexReader = generator.declareClassField("reader", generator.getModel()._ref(FieldReader.class)); + + 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 = nrClass.staticRef("INSTANCE"); + + jc._then().assign(complexReader, expr); + jc._else().assign(complexReader, nullReader); + + HoldingContainer hc = new HoldingContainer(e.getMajorType(), complexReader, null, null, false); return hc; - //eval.assign(out.getHolder().ref("reader"), expr); - }else{ - if(seg != null){ + // //eval.assign(out.getHolder().ref("reader"), expr); + } else { + if (seg != null) { eval.add(expr.invoke("read").arg(JExpr.lit(seg.getArraySegment().getIndex())).arg(out.getHolder())); - }else{ - + } else { eval.add(expr.invoke("read").arg(out.getHolder())); } } } - - - return out; } @@ -453,9 +477,9 @@ public class EvaluationVisitor { LogicalExpression child = e.getChild(); // Preconditions.checkArgument(child.getMajorType().equals(Types.REQUIRED_BOOLEAN)); HoldingContainer hc = child.accept(this, generator); - if(e.isReturnTrueOnOne()){ + if (e.isReturnTrueOnOne()) { generator.getEvalBlock()._return(hc.getValue().eq(JExpr.lit(1))); - }else{ + } else { generator.getEvalBlock()._return(hc.getValue()); } @@ -470,7 +494,8 @@ public class EvaluationVisitor { JType holderType = generator.getHolderType(majorType); JVar var = generator.declareClassField("string", holderType); JExpression stringLiteral = JExpr.lit(e.value); - setup.assign(var, generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getVarCharHolder").arg(stringLiteral)); + setup.assign(var, + generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getVarCharHolder").arg(stringLiteral)); return new HoldingContainer(majorType, var, null, null); } @@ -483,12 +508,16 @@ public class EvaluationVisitor { JVar var = generator.declareClassField("intervalday", holderType); JExpression dayLiteral = JExpr.lit(e.getIntervalDay()); JExpression millisLiteral = JExpr.lit(e.getIntervalMillis()); - setup.assign(var, generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getIntervalDayHolder").arg(dayLiteral).arg(millisLiteral)); + setup.assign( + var, + generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getIntervalDayHolder").arg(dayLiteral) + .arg(millisLiteral)); return new HoldingContainer(majorType, var, null, null); } @Override - public HoldingContainer visitDecimal9Constant(Decimal9Expression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitDecimal9Constant(Decimal9Expression e, ClassGenerator<?> generator) + throws RuntimeException { MajorType majorType = e.getMajorType(); JBlock setup = generator.getBlock(BlockType.SETUP); JType holderType = generator.getHolderType(majorType); @@ -496,12 +525,16 @@ public class EvaluationVisitor { JExpression valueLiteral = JExpr.lit(e.getIntFromDecimal()); JExpression scaleLiteral = JExpr.lit(e.getScale()); JExpression precisionLiteral = JExpr.lit(e.getPrecision()); - setup.assign(var, generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getDecimal9Holder").arg(valueLiteral).arg(scaleLiteral).arg(precisionLiteral)); + setup.assign( + var, + generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getDecimal9Holder").arg(valueLiteral) + .arg(scaleLiteral).arg(precisionLiteral)); return new HoldingContainer(majorType, var, null, null); } @Override - public HoldingContainer visitDecimal18Constant(Decimal18Expression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitDecimal18Constant(Decimal18Expression e, ClassGenerator<?> generator) + throws RuntimeException { MajorType majorType = e.getMajorType(); JBlock setup = generator.getBlock(BlockType.SETUP); JType holderType = generator.getHolderType(majorType); @@ -509,7 +542,10 @@ public class EvaluationVisitor { JExpression valueLiteral = JExpr.lit(e.getLongFromDecimal()); JExpression scaleLiteral = JExpr.lit(e.getScale()); JExpression precisionLiteral = JExpr.lit(e.getPrecision()); - setup.assign(var, generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getDecimal18Holder").arg(valueLiteral).arg(scaleLiteral).arg(precisionLiteral)); + setup.assign( + var, + generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getDecimal18Holder").arg(valueLiteral) + .arg(scaleLiteral).arg(precisionLiteral)); return new HoldingContainer(majorType, var, null, null); } @@ -521,7 +557,8 @@ public class EvaluationVisitor { JType holderType = generator.getHolderType(majorType); JVar var = generator.declareClassField("dec28", holderType); JExpression stringLiteral = JExpr.lit(e.getBigDecimal().toString()); - setup.assign(var, generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getDecimal28Holder").arg(stringLiteral)); + setup.assign(var, + generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getDecimal28Holder").arg(stringLiteral)); return new HoldingContainer(majorType, var, null, null); } @@ -533,34 +570,34 @@ public class EvaluationVisitor { JType holderType = generator.getHolderType(majorType); JVar var = generator.declareClassField("dec38", holderType); JExpression stringLiteral = JExpr.lit(e.getBigDecimal().toString()); - setup.assign(var, generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getVarCharHolder").arg(stringLiteral)); + setup.assign(var, + generator.getModel().ref(ValueHolderHelper.class).staticInvoke("getVarCharHolder").arg(stringLiteral)); return new HoldingContainer(majorType, var, null, null); } @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"); + 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 { + public HoldingContainer visitConvertExpression(ConvertExpression e, ClassGenerator<?> value) + throws RuntimeException { String convertFunctionName = e.getConvertFunction() + e.getEncodingType(); List<LogicalExpression> newArgs = Lists.newArrayList(); - newArgs.add(e.getInput()); //input_expr + newArgs.add(e.getInput()); // input_expr FunctionCall fc = new FunctionCall(convertFunctionName, newArgs, e.getPosition()); return fc.accept(this, value); } } - private class ConstantFilter extends EvalVisitor { private Set<LogicalExpression> constantBoundaries; - public ConstantFilter(Set<LogicalExpression> constantBoundaries) { super(); this.constantBoundaries = constantBoundaries; @@ -568,17 +605,18 @@ public class EvaluationVisitor { @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"); + throw new UnsupportedOperationException("FunctionCall is not expected here. " + + "It should have been converted to FunctionHolderExpression in materialization"); } @Override - public HoldingContainer visitFunctionHolderExpression(FunctionHolderExpression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitFunctionHolderExpression(FunctionHolderExpression e, ClassGenerator<?> generator) + throws RuntimeException { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitFunctionHolderExpression(e, generator); - //generator.getMappingSet().exitConstant(); - //return c; + // generator.getMappingSet().exitConstant(); + // return c; return renderConstantExpression(generator, c); } else if (generator.getMappingSet().isWithinConstant()) { return super.visitFunctionHolderExpression(e, generator).setConstant(true); @@ -607,8 +645,8 @@ public class EvaluationVisitor { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitSchemaPath(e, generator); - //generator.getMappingSet().exitConstant(); - //return c; + // generator.getMappingSet().exitConstant(); + // return c; return renderConstantExpression(generator, c); } else if (generator.getMappingSet().isWithinConstant()) { return super.visitSchemaPath(e, generator).setConstant(true); @@ -622,8 +660,8 @@ public class EvaluationVisitor { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitLongConstant(e, generator); - //generator.getMappingSet().exitConstant(); - //return c; + // generator.getMappingSet().exitConstant(); + // return c; return renderConstantExpression(generator, c); } else if (generator.getMappingSet().isWithinConstant()) { return super.visitLongConstant(e, generator).setConstant(true); @@ -632,9 +670,9 @@ public class EvaluationVisitor { } } - @Override - public HoldingContainer visitDecimal9Constant(Decimal9Expression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitDecimal9Constant(Decimal9Expression e, ClassGenerator<?> generator) + throws RuntimeException { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitDecimal9Constant(e, generator); @@ -647,7 +685,8 @@ public class EvaluationVisitor { } @Override - public HoldingContainer visitDecimal18Constant(Decimal18Expression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitDecimal18Constant(Decimal18Expression e, ClassGenerator<?> generator) + throws RuntimeException { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitDecimal18Constant(e, generator); @@ -660,7 +699,8 @@ public class EvaluationVisitor { } @Override - public HoldingContainer visitDecimal28Constant(Decimal28Expression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitDecimal28Constant(Decimal28Expression e, ClassGenerator<?> generator) + throws RuntimeException { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitDecimal28Constant(e, generator); @@ -673,7 +713,8 @@ public class EvaluationVisitor { } @Override - public HoldingContainer visitDecimal38Constant(Decimal38Expression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitDecimal38Constant(Decimal38Expression e, ClassGenerator<?> generator) + throws RuntimeException { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitDecimal38Constant(e, generator); @@ -690,8 +731,8 @@ public class EvaluationVisitor { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitIntConstant(e, generator); - //generator.getMappingSet().exitConstant(); - //return c; + // generator.getMappingSet().exitConstant(); + // return c; return renderConstantExpression(generator, c); } else if (generator.getMappingSet().isWithinConstant()) { return super.visitIntConstant(e, generator).setConstant(true); @@ -714,7 +755,6 @@ public class EvaluationVisitor { } } - @Override public HoldingContainer visitTimeConstant(TimeExpression e, ClassGenerator<?> generator) throws RuntimeException { if (constantBoundaries.contains(e)) { @@ -730,7 +770,8 @@ public class EvaluationVisitor { } @Override - public HoldingContainer visitIntervalYearConstant(IntervalYearExpression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitIntervalYearConstant(IntervalYearExpression e, ClassGenerator<?> generator) + throws RuntimeException { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitIntervalYearConstant(e, generator); @@ -744,7 +785,8 @@ public class EvaluationVisitor { } @Override - public HoldingContainer visitTimeStampConstant(TimeStampExpression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitTimeStampConstant(TimeStampExpression e, ClassGenerator<?> generator) + throws RuntimeException { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitTimeStampConstant(e, generator); @@ -758,12 +800,13 @@ public class EvaluationVisitor { } @Override - public HoldingContainer visitDoubleConstant(DoubleExpression e, ClassGenerator<?> generator) throws RuntimeException { + public HoldingContainer visitDoubleConstant(DoubleExpression e, ClassGenerator<?> generator) + throws RuntimeException { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitDoubleConstant(e, generator); - //generator.getMappingSet().exitConstant(); - //return c; + // generator.getMappingSet().exitConstant(); + // return c; return renderConstantExpression(generator, c); } else if (generator.getMappingSet().isWithinConstant()) { return super.visitDoubleConstant(e, generator).setConstant(true); @@ -778,8 +821,8 @@ public class EvaluationVisitor { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitBooleanConstant(e, generator); - //generator.getMappingSet().exitConstant(); - //return c; + // generator.getMappingSet().exitConstant(); + // return c; return renderConstantExpression(generator, c); } else if (generator.getMappingSet().isWithinConstant()) { return super.visitBooleanConstant(e, generator).setConstant(true); @@ -788,14 +831,13 @@ public class EvaluationVisitor { } } - @Override public HoldingContainer visitUnknown(LogicalExpression e, ClassGenerator<?> generator) throws RuntimeException { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitUnknown(e, generator); - //generator.getMappingSet().exitConstant(); - //return c; + // generator.getMappingSet().exitConstant(); + // return c; return renderConstantExpression(generator, c); } else if (generator.getMappingSet().isWithinConstant()) { return super.visitUnknown(e, generator).setConstant(true); @@ -810,8 +852,8 @@ public class EvaluationVisitor { if (constantBoundaries.contains(e)) { generator.getMappingSet().enterConstant(); HoldingContainer c = super.visitQuotedStringConstant(e, generator); - //generator.getMappingSet().exitConstant(); - //return c; + // generator.getMappingSet().exitConstant(); + // return c; return renderConstantExpression(generator, c); } else if (generator.getMappingSet().isWithinConstant()) { return super.visitQuotedStringConstant(e, generator).setConstant(true); @@ -820,7 +862,6 @@ public class EvaluationVisitor { } } - @Override public HoldingContainer visitIntervalDayConstant(IntervalDayExpression e, ClassGenerator<?> generator) throws RuntimeException { @@ -836,14 +877,16 @@ public class EvaluationVisitor { } } - /* Get a HoldingContainer for a constant expression. The returned HoldingContainder will indicate it's for - * a constant expression. - * */ - private HoldingContainer renderConstantExpression(ClassGenerator<?> generator, HoldingContainer input){ + /* + * Get a HoldingContainer for a constant expression. The returned + * HoldingContainder will indicate it's for a constant expression. + */ + private HoldingContainer renderConstantExpression(ClassGenerator<?> generator, HoldingContainer input) { JVar fieldValue = generator.declareClassField("constant", generator.getHolderType(input.getMajorType())); generator.getEvalBlock().assign(fieldValue, input.getHolder()); generator.getMappingSet().exitConstant(); - return new HoldingContainer(input.getMajorType(), fieldValue, fieldValue.ref("value"), fieldValue.ref("isSet")).setConstant(true); + return new HoldingContainer(input.getMajorType(), fieldValue, fieldValue.ref("value"), fieldValue.ref("isSet")) + .setConstant(true); } } } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/ExpressionTreeMaterializer.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/ExpressionTreeMaterializer.java index fca743bb2..99d423f45 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/ExpressionTreeMaterializer.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/ExpressionTreeMaterializer.java @@ -153,9 +153,9 @@ public class ExpressionTreeMaterializer { ( parmType.getMode().equals(TypeProtos.DataMode.OPTIONAL) || matchedFuncHolder.getNullHandling() == FunctionTemplate.NullHandling.NULL_IF_NULL)) { argsWithCast.add(new TypedNullConstant(parmType)); - } else if (Types.softEquals(parmType, currentArg.getMajorType(), matchedFuncHolder.getNullHandling() == - FunctionTemplate.NullHandling.NULL_IF_NULL)) { - //Case 2: argument and parameter matches. Do nothing. + } else if (Types.softEquals(parmType, currentArg.getMajorType(), matchedFuncHolder.getNullHandling() == FunctionTemplate.NullHandling.NULL_IF_NULL) || + matchedFuncHolder.isFieldReader(i)) { + //Case 2: argument and parameter matches, or parameter is FieldReader. Do nothing. argsWithCast.add(currentArg); } else { //Case 3: insert cast if param type is different from arg type. 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 ea4e9f65a..5d5acf0bc 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 @@ -24,19 +24,25 @@ import java.util.Map; import org.apache.drill.common.expression.LogicalExpression; import org.apache.drill.common.types.TypeProtos; import org.apache.drill.common.types.TypeProtos.MajorType; +import org.apache.drill.common.types.TypeProtos.MinorType; import org.apache.drill.common.types.Types; import org.apache.drill.exec.expr.ClassGenerator; import org.apache.drill.exec.expr.ClassGenerator.BlockType; import org.apache.drill.exec.expr.ClassGenerator.HoldingContainer; +import org.apache.drill.exec.expr.TypeHelper; import org.apache.drill.exec.expr.annotations.FunctionTemplate; import org.apache.drill.exec.expr.annotations.FunctionTemplate.FunctionScope; import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling; +import org.apache.drill.exec.vector.complex.impl.NullableBigIntSingularReaderImpl; +import org.apache.drill.exec.vector.complex.reader.FieldReader; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.sun.codemodel.JBlock; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JType; import com.sun.codemodel.JVar; public abstract class DrillFuncHolder { @@ -126,7 +132,14 @@ public abstract class DrillFuncHolder { ValueReference parameter = parameters[i]; HoldingContainer inputVariable = inputVariables[i]; - sub.decl(inputVariable.getHolder().type(), parameter.name, inputVariable.getHolder()); + if (parameter.isFieldReader && ! Types.isComplex(inputVariable.getMajorType()) && ! Types.isRepeated(inputVariable.getMajorType())) { + JType singularReaderClass = g.getModel()._ref(TypeHelper.getSingularReaderImpl(inputVariable.getMajorType().getMinorType(), + inputVariable.getMajorType().getMode())); + JType fieldReadClass = g.getModel()._ref(FieldReader.class); + sub.decl(fieldReadClass, parameter.name, JExpr._new(singularReaderClass).arg(inputVariable.getHolder())); + } else { + sub.decl(inputVariable.getHolder().type(), parameter.name, inputVariable.getHolder()); + } } } @@ -181,6 +194,10 @@ public abstract class DrillFuncHolder { return this.parameters[i].isConstant; } + public boolean isFieldReader(int i) { + return this.parameters[i].isFieldReader; + } + public MajorType getReturnType(List<LogicalExpression> args) { if (nullHandling == NullHandling.NULL_IF_NULL) { // if any one of the input types is nullable, then return nullable return type @@ -220,6 +237,7 @@ public abstract class DrillFuncHolder { MajorType type; String name; boolean isConstant; + boolean isFieldReader; public ValueReference(MajorType type, String name) { super(); @@ -228,6 +246,7 @@ public abstract class DrillFuncHolder { this.type = type; this.name = name; isConstant = false; + this.isFieldReader = false; } public void setConstant(boolean isConstant) { @@ -239,6 +258,13 @@ public abstract class DrillFuncHolder { return "ValueReference [type=" + Types.toString(type) + ", name=" + name + "]"; } + public static ValueReference createFieldReaderRef(String name) { + MajorType type = Types.required(MinorType.LATE); + ValueReference ref = new ValueReference(type, name); + ref.isFieldReader = true; + + return ref; + } } public static class WorkspaceReference { 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 44210914d..7f5a33536 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 @@ -36,6 +36,7 @@ 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.holders.ValueHolder; +import org.apache.drill.exec.vector.complex.reader.FieldReader; import org.codehaus.commons.compiler.CompileException; import org.codehaus.janino.Java.CompilationUnit; import org.codehaus.janino.Parser; @@ -52,9 +53,9 @@ import com.google.common.io.Resources; */ public class FunctionConverter { static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(FunctionConverter.class); - + private Map<String, CompilationUnit> functionUnits = Maps.newHashMap(); - + private CompilationUnit get(Class<?> c) throws IOException{ String path = c.getName(); path = path.replaceFirst("\\$.*", ""); @@ -70,7 +71,7 @@ public class FunctionConverter { throw new IOException(String.format("Failure trying to located source code for Class %s, tried to read on classpath location %s", c.getName(), path)); } String body = IO.toString(is); - + //TODO: Hack to remove annotations so Janino doesn't choke. Need to reconsider this problem... body = body.replaceAll("@\\w+(?:\\([^\\\\]*?\\))?", ""); try{ @@ -81,11 +82,11 @@ public class FunctionConverter { logger.warn("Failure while parsing function class:\n{}", body, e); return null; } - + } - + } - + public <T extends DrillFunc> DrillFuncHolder getHolder(Class<T> clazz){ FunctionTemplate template = clazz.getAnnotation(FunctionTemplate.class); if(template == null){ @@ -100,16 +101,16 @@ public class FunctionConverter { // start by getting field information. List<ValueReference> params = Lists.newArrayList(); List<WorkspaceReference> workspaceFields = Lists.newArrayList(); - + ValueReference outputField = null; - - + + for(Field field : clazz.getDeclaredFields()){ Param param = field.getAnnotation(Param.class); Output output = field.getAnnotation(Output.class); Workspace workspace = field.getAnnotation(Workspace.class); - + int i =0; if(param != null) i++; if(output != null) i++; @@ -121,12 +122,18 @@ public class FunctionConverter { } if(param != null || output != null){ - + + // Special processing for @Param FieldReader + if (param != null && FieldReader.class.isAssignableFrom(field.getType())) { + params.add(ValueReference.createFieldReaderRef(field.getName())); + continue; + } + // check that param and output are value holders. if(!ValueHolder.class.isAssignableFrom(field.getType())){ return failure(String.format("The field doesn't holds value of type %s which does not implement the ValueHolder interface. All fields of type @Param or @Output must extend this interface..", field.getType()), clazz, field); } - + // get the type field from the value holder. MajorType type = null; try{ @@ -134,34 +141,34 @@ public class FunctionConverter { }catch(Exception e){ return failure("Failure while trying to access the ValueHolder's TYPE static variable. All ValueHolders must contain a static TYPE variable that defines their MajorType.", e, clazz, field.getName()); } - - + + ValueReference p = new ValueReference(type, field.getName()); if(param != null){ if (param.constant()) { p.setConstant(true); } params.add(p); - }else{ + }else{ if(outputField != null){ return failure("You've declared more than one @Output field. You must declare one and only @Output field per Function class.", clazz, field); }else{ - outputField = p; - + outputField = p; + } - + } - + }else{ // workspace work. // logger.debug("Found workspace field {}:{}", field.getType(), field.getName()); //workspaceFields.add(new WorkspaceReference(field.getType(), field.getName())); WorkspaceReference wsReference = new WorkspaceReference(field.getType(), field.getName()); - + if (template.scope() == FunctionScope.POINT_AGGREGATE && !ValueHolder.class.isAssignableFrom(field.getType()) ) { - return failure(String.format("Aggregate function '%s' workspace variable '%s' is of type '%s'. Please change it to Holder type.", template.name(), field.getName(), field.getType()), clazz, field); + return failure(String.format("Aggregate function '%s' workspace variable '%s' is of type '%s'. Please change it to Holder type.", template.name(), field.getName(), field.getType()), clazz, field); } - + //If the workspace var is of Holder type, get its MajorType and assign to WorkspaceReference. if(ValueHolder.class.isAssignableFrom(field.getType())){ MajorType majorType = null; @@ -172,18 +179,18 @@ public class FunctionConverter { } wsReference.setMajorType(majorType); } - + workspaceFields.add(wsReference); } - + } - - + + // if(!workspaceFields.isEmpty()) return failure("This function declares one or more workspace fields. However, those have not yet been implemented.", clazz); if(outputField == null) return failure("This function declares zero output fields. A function must declare one output field.", clazz); - - // get function body. - + + // get function body. + CompilationUnit cu; try { cu = get(clazz); @@ -191,8 +198,8 @@ public class FunctionConverter { } catch (IOException e) { return failure("Failure while getting class body.", e, clazz); } - - + + try{ Map<String, String> methods = MethodGrabbingVisitor.getMethods(cu, clazz); List<String> imports = ImportGrabber.getMethods(cu); @@ -238,11 +245,11 @@ public class FunctionConverter { }catch(Exception | NoSuchFieldError | AbstractMethodError ex){ return failure("Failure while creating function holder.", ex, clazz); } - + } - - - + + + private String getClassBody(Class<?> c) throws CompileException, IOException{ String path = c.getName(); path = path.replaceFirst("\\$.*", ""); @@ -255,28 +262,28 @@ public class FunctionConverter { throw new IOException(String.format("Failure trying to located source code for Class %s, tried to read on classpath location %s", c.getName(), path)); } String body = IO.toString(is); - + //TODO: Hack to remove annotations so Janino doesn't choke. Need to reconsider this problem... //return body.replaceAll("@(?:Output|Param|Workspace|Override|SuppressWarnings\\([^\\\\]*?\\)|FunctionTemplate\\([^\\\\]*?\\))", ""); return body.replaceAll("@(?:\\([^\\\\]*?\\))?", ""); } - + } - - - + + + @SuppressWarnings("unchecked") private <T> T getStaticFieldValue(String fieldName, Class<?> valueType, Class<T> c) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{ Field f = valueType.getDeclaredField(fieldName); Object val = f.get(null); return (T) val; } - + private static DrillFuncHolder failure(String message, Throwable t, Class<?> clazz, String fieldName){ logger.warn("Failure loading function class {}, field {}. " + message, clazz.getName(), fieldName, t); return null; - } - + } + private DrillFuncHolder failure(String message, Class<?> clazz, String fieldName){ logger.warn("Failure loading function class {}, field {}. " + message, clazz.getName(), fieldName); return null; @@ -291,11 +298,11 @@ public class FunctionConverter { logger.warn("Failure loading function class [{}]. Message: {}", clazz.getName(), message, t); return null; } - + private DrillFuncHolder failure(String message, Class<?> clazz, Field field){ return failure(message, clazz, field.getName()); } - - + + } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/conv/JsonConvertTo.java b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/conv/JsonConvertTo.java new file mode 100644 index 000000000..ca1e01fe8 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/expr/fn/impl/conv/JsonConvertTo.java @@ -0,0 +1,81 @@ +/** + * 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.impl.conv; + +import java.io.ByteArrayOutputStream; + +import io.netty.buffer.ByteBuf; + +import org.apache.drill.exec.expr.DrillSimpleFunc; +import org.apache.drill.exec.expr.annotations.FunctionTemplate; +import org.apache.drill.exec.expr.annotations.Output; +import org.apache.drill.exec.expr.annotations.Param; +import org.apache.drill.exec.expr.annotations.Workspace; +import org.apache.drill.exec.expr.annotations.FunctionTemplate.FunctionScope; +import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling; +import org.apache.drill.exec.expr.holders.VarBinaryHolder; +import org.apache.drill.exec.expr.holders.VarCharHolder; +import org.apache.drill.exec.record.RecordBatch; +import org.apache.drill.exec.vector.complex.fn.JsonWriter; +import org.apache.drill.exec.vector.complex.reader.FieldReader; + +import com.google.common.base.Charsets; + +public class JsonConvertTo { + + static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(JsonConvertTo.class); + + private JsonConvertTo(){} + + @FunctionTemplate(name = "convert_toJSON", scope = FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL) + public static class ConvertToJson implements DrillSimpleFunc{ + + @Param FieldReader input; + @Output VarBinaryHolder out; + @Workspace ByteBuf buffer; + + public void setup(RecordBatch incoming){ + buffer = org.apache.drill.exec.util.ConvertUtil.createBuffer(256); + } + + public void eval(){ + out.buffer = buffer; + out.start = 0; + + java.io.ByteArrayOutputStream stream = new java.io.ByteArrayOutputStream(); + try { + org.apache.drill.exec.vector.complex.fn.JsonWriter jsonWriter = new org.apache.drill.exec.vector.complex.fn.JsonWriter(stream, true); + + jsonWriter.write(input); + } catch (Exception e) { + System.out.println(" msg = " + e.getMessage() + " trace : " + e.getStackTrace()); + } + + byte [] bytea = stream.toByteArray(); + + if (bytea.length > buffer.capacity()) { + buffer = org.apache.drill.exec.util.ConvertUtil.createBuffer(bytea.length); + out.buffer = buffer; + } + + out.buffer.setBytes(out.start, bytea); + out.end = bytea.length; + } + } +} diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/record/SimpleVectorWrapper.java b/exec/java-exec/src/main/java/org/apache/drill/exec/record/SimpleVectorWrapper.java index 692fe62f0..8a2312a93 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/record/SimpleVectorWrapper.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/record/SimpleVectorWrapper.java @@ -19,6 +19,7 @@ package org.apache.drill.exec.record; import org.apache.drill.common.expression.PathSegment; import org.apache.drill.common.expression.SchemaPath; +import org.apache.drill.common.types.TypeProtos.DataMode; import org.apache.drill.exec.vector.ValueVector; import org.apache.drill.exec.vector.complex.AbstractContainerVector; import org.apache.drill.exec.vector.complex.MapVector; @@ -115,6 +116,7 @@ public class SimpleVectorWrapper<T extends ValueVector> implements VectorWrapper if(child.isArray() && child.isLastPath()){ builder.remainder(child); builder.withIndex(); + builder.finalType(v.getField().getType().toBuilder().setMode(DataMode.OPTIONAL).build()); return builder.build(); }else{ return null; diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/record/TypedFieldId.java b/exec/java-exec/src/main/java/org/apache/drill/exec/record/TypedFieldId.java index 24a8251ac..22aa73141 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/record/TypedFieldId.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/record/TypedFieldId.java @@ -121,6 +121,7 @@ public class TypedFieldId { final IntArrayList ids = new IntArrayList(); MajorType finalType; MajorType intermediateType; + MajorType secondaryFinal; PathSegment remainder; boolean hyperReader = false; boolean withIndex = false; @@ -150,6 +151,11 @@ public class TypedFieldId { return this; } + public Builder secondaryFinal(MajorType secondaryFinal) { + this.secondaryFinal = secondaryFinal; + return this; + } + public Builder intermediateType(MajorType intermediateType){ this.intermediateType = intermediateType; return this; @@ -160,15 +166,17 @@ public class TypedFieldId { Preconditions.checkNotNull(finalType); if(intermediateType == null) intermediateType = finalType; + if (secondaryFinal == null) secondaryFinal = finalType; + MajorType actualFinalType = finalType; - MajorType secondaryFinal = finalType; + //MajorType secondaryFinal = finalType; // if this has an index, switch to required type for output - if(withIndex && intermediateType == finalType) actualFinalType = finalType.toBuilder().setMode(DataMode.REQUIRED).build(); + //if(withIndex && intermediateType == finalType) actualFinalType = finalType.toBuilder().setMode(DataMode.REQUIRED).build(); // if this isn't a direct access, switch the final type to nullable as offsets may be null. // TODO: there is a bug here with some things. - if(intermediateType != finalType) actualFinalType = finalType.toBuilder().setMode(DataMode.OPTIONAL).build(); + //if(intermediateType != finalType) actualFinalType = finalType.toBuilder().setMode(DataMode.OPTIONAL).build(); return new TypedFieldId(intermediateType, secondaryFinal, actualFinalType, hyperReader, remainder, ids.toArray()); } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/resolver/TypeCastRules.java b/exec/java-exec/src/main/java/org/apache/drill/exec/resolver/TypeCastRules.java index 2f6bf38a0..3c2055525 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/resolver/TypeCastRules.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/resolver/TypeCastRules.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Set; import org.apache.drill.common.expression.FunctionCall; +import org.apache.drill.common.types.Types; import org.apache.drill.common.types.TypeProtos.DataMode; import org.apache.drill.common.types.TypeProtos.MajorType; import org.apache.drill.common.types.TypeProtos.MinorType; @@ -796,6 +797,14 @@ public class TypeCastRules { MajorType argType = call.args.get(i).getMajorType(); MajorType parmType = holder.getParmMajorType(i); + //@Param FieldReader will match any type + if (holder.isFieldReader(i)) { +// if (Types.isComplex(call.args.get(i).getMajorType()) ||Types.isRepeated(call.args.get(i).getMajorType()) ) + continue; +// else +// return -1; + } + if (!TypeCastRules.isCastable(argType, parmType, holder.getNullHandling())) { return -1; } @@ -832,7 +841,7 @@ public class TypeCastRules { } // Check null vs non-null, using same logic as that in Types.softEqual() // Only when the function uses NULL_IF_NULL, nullable and non-nullable are inter-changable. - // Otherwise, the function implementation is not a match. + // Otherwise, the function implementation is not a match. if (argType.getMode() != parmType.getMode()) { // TODO - this does not seem to do what it is intended to // if (!((holder.getNullHandling() == NullHandling.NULL_IF_NULL) && diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/AbstractContainerVector.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/AbstractContainerVector.java index ab1d2707d..4f7cf52cf 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/AbstractContainerVector.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/AbstractContainerVector.java @@ -18,7 +18,9 @@ package org.apache.drill.exec.vector.complex; import org.apache.drill.common.expression.PathSegment; +import org.apache.drill.common.types.TypeProtos.DataMode; import org.apache.drill.common.types.TypeProtos.MajorType; +import org.apache.drill.common.types.TypeProtos.MinorType; import org.apache.drill.exec.record.TypedFieldId; import org.apache.drill.exec.vector.ValueVector; @@ -49,11 +51,13 @@ public abstract class AbstractContainerVector implements ValueVector{ if(seg.isArray()){ if(seg.isLastPath()){ - if(addToBreadCrumb) builder.intermediateType(this.getField().getType()); + + //if(addToBreadCrumb) builder.intermediateType(this.getField().getType()); + return builder // .remainder(seg) // - .finalType(this.getField().getType()) // .withIndex() // + .finalType(getLastPathType()) // .build(); }else{ if(addToBreadCrumb){ @@ -72,30 +76,50 @@ public abstract class AbstractContainerVector implements ValueVector{ VectorWithOrdinal vord = getVectorWithOrdinal(seg.isArray() ? null : seg.getNameSegment().getPath()); if(vord == null) return null; + ValueVector v = vord.vector; if(addToBreadCrumb){ - builder.intermediateType(this.getField().getType()); + //builder.intermediateType(this.getField().getType()); + builder.intermediateType(v.getField().getType()); builder.addId(vord.ordinal); } - ValueVector v = vord.vector; - if(v instanceof AbstractContainerVector){ // we're looking for a multi path. AbstractContainerVector c = (AbstractContainerVector) v; return c.getFieldIdIfMatches(builder, addToBreadCrumb, seg.getChild()); }else{ - if(seg.isLastPath()){ + if (seg != null && seg.isLastPath() && ! seg.isArray()) { if(addToBreadCrumb) builder.intermediateType(v.getField().getType()); return builder.finalType(v.getField().getType()).build(); + }else if(seg != null && seg.isLastPath() && seg.isArray()){ + //if(addToBreadCrumb) builder.intermediateType(v.getField().getType()); + return builder + .finalType(v.getField().getType().toBuilder().setMode(DataMode.OPTIONAL).build()) + .build(); }else{ - logger.warn("You tried to request a complex type inside a scalar object."); + logger.warn("You tried to request a complex type inside a scalar object or path or type is wrong."); return null; } } } + private MajorType getLastPathType() { + if((this.getField().getType().getMinorType() == MinorType.LIST && + this.getField().getType().getMode() == DataMode.REPEATED)) { // Use Repeated scalar type instead of Required List. + VectorWithOrdinal vord = getVectorWithOrdinal(null); + ValueVector v = vord.vector; + if (! (v instanceof AbstractContainerVector)) + return v.getField().getType(); + } else if (this.getField().getType().getMinorType() == MinorType.MAP && + this.getField().getType().getMode() == DataMode.REPEATED) { // Use Required Map + return this.getField().getType().toBuilder().setMode(DataMode.REQUIRED).build(); + } + + return this.getField().getType(); + } + protected boolean supportsDirectRead(){ return false; } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/MapVector.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/MapVector.java index 9a84ee871..8ea27961b 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/MapVector.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/MapVector.java @@ -351,7 +351,8 @@ public class MapVector extends AbstractContainerVector { @Override public FieldReader getReader() { - return new SingleMapReaderImpl(MapVector.this); + //return new SingleMapReaderImpl(MapVector.this); + return reader; } } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/RepeatedMapVector.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/RepeatedMapVector.java index bcf8ad71f..f41cfdaa6 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/RepeatedMapVector.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/RepeatedMapVector.java @@ -248,10 +248,10 @@ public class RepeatedMapVector extends AbstractContainerVector implements Repeat //todo: make these bulk copies for(int i = holder.start; i < holder.end; i++, newIndex++){ for(TransferPair p : pairs){ - if(!p.copyValueSafe(from, to)) return false; + if(!p.copyValueSafe(i, newIndex)) return false; } } - if(!this.to.offsets.getMutator().setSafe(to, newIndex)) return false; + if(!this.to.offsets.getMutator().setSafe(to+1, newIndex)) return false; return true; } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/fn/JsonWriter.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/fn/JsonWriter.java index 0624eceb1..de52b73e5 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/fn/JsonWriter.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/fn/JsonWriter.java @@ -118,10 +118,13 @@ public class JsonWriter { break; case MAP: gen.writeStartObject(); - for(String name : reader){ - if(reader.isSet()){ - gen.writeFieldName(name); - writeValue(reader.reader(name)); + if (reader.isSet()) { + for(String name : reader){ + FieldReader childReader = reader.reader(name); + if(childReader.isSet()){ + gen.writeFieldName(name); + writeValue(childReader); + } } } gen.writeEndObject(); diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/RepeatedListReaderImpl.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/RepeatedListReaderImpl.java index c555f35ff..66f658dc8 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/RepeatedListReaderImpl.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/RepeatedListReaderImpl.java @@ -98,7 +98,10 @@ public class RepeatedListReaderImpl extends AbstractFieldReader{ public FieldReader reader(){ if(reader == null){ reader = container.get(name, ValueVector.class).getAccessor().getReader(); - reader.setPosition(currentOffset); + if (currentOffset == NO_VALUES) + reader = NullReader.INSTANCE; + else + reader.setPosition(currentOffset); } return reader; } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/RepeatedMapReaderImpl.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/RepeatedMapReaderImpl.java index ab778ff3f..8f788af43 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/RepeatedMapReaderImpl.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/RepeatedMapReaderImpl.java @@ -77,6 +77,7 @@ import org.apache.hadoop.io.Text; + import java.util.Map; import java.util.Map.Entry; @@ -117,6 +118,13 @@ public class RepeatedMapReaderImpl extends AbstractFieldReader{ return reader; } + public FieldReader reader() { + if (currentOffset == NO_VALUES) + return NullReader.INSTANCE; + + setChildrenPosition(currentOffset); + return new SingleLikeRepeatedMapReaderImpl(vector, this); + } private int currentOffset; private int maxOffset; @@ -163,6 +171,10 @@ public class RepeatedMapReaderImpl extends AbstractFieldReader{ } } + public boolean isNull() { + return currentOffset == NO_VALUES; + } + @Override public Object readObject() { return vector.getAccessor().getObject(idx()); @@ -178,7 +190,7 @@ public class RepeatedMapReaderImpl extends AbstractFieldReader{ @Override public boolean isSet() { - return false; + return true; } public void copyAsValue(MapWriter writer){ diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/SingleLikeRepeatedMapReaderImpl.java b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/SingleLikeRepeatedMapReaderImpl.java new file mode 100644 index 000000000..3f89a9f10 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/complex/impl/SingleLikeRepeatedMapReaderImpl.java @@ -0,0 +1,89 @@ +/** + * 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.vector.complex.impl; + +import java.util.Iterator; + +import org.apache.drill.common.types.TypeProtos.MajorType; +import org.apache.drill.common.types.TypeProtos.MinorType; +import org.apache.drill.common.types.Types; +import org.apache.drill.exec.vector.complex.RepeatedMapVector; +import org.apache.drill.exec.vector.complex.reader.FieldReader; +import org.apache.drill.exec.vector.complex.writer.BaseWriter.MapWriter; + +public class SingleLikeRepeatedMapReaderImpl extends AbstractFieldReader{ + + private RepeatedMapReaderImpl delegate; + + public SingleLikeRepeatedMapReaderImpl(RepeatedMapVector vector, FieldReader delegate) { + this.delegate = (RepeatedMapReaderImpl) delegate; + } + + @Override + public int size() { + throw new UnsupportedOperationException("You can't call size on a single map reader."); + } + + @Override + public boolean next() { + throw new UnsupportedOperationException("You can't call next on a single map reader."); + } + + @Override + public MajorType getType() { + return Types.required(MinorType.MAP); + } + + + @Override + public void copyAsValue(MapWriter writer) { + delegate.copyAsValueSingle(writer); + } + + public void copyAsValueSingle(MapWriter writer){ + delegate.copyAsValueSingle(writer); + } + + @Override + public FieldReader reader(String name) { + return delegate.reader(name); + } + + @Override + public void setPosition(int index) { + delegate.setPosition(index); + } + + @Override + public Object readObject() { + return delegate.readObject(); + } + + @Override + public Iterator<String> iterator() { + return delegate.iterator(); + } + + @Override + public boolean isSet() { + return ! delegate.isNull(); + } + + +} diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/vector/complex/writer/TestComplexTypeReader.java b/exec/java-exec/src/test/java/org/apache/drill/exec/vector/complex/writer/TestComplexTypeReader.java new file mode 100644 index 000000000..74c724d06 --- /dev/null +++ b/exec/java-exec/src/test/java/org/apache/drill/exec/vector/complex/writer/TestComplexTypeReader.java @@ -0,0 +1,167 @@ +/** + * 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.vector.complex.writer; + +import org.apache.drill.BaseTestQuery; +import org.junit.Test; + +public class TestComplexTypeReader extends BaseTestQuery{ + static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(TestComplexTypeReader.class); + + @Test + // Repeated map (map) -> json. + public void testX() throws Exception{ + test("select convert_to(z[0], 'JSON') from cp.`jsoninput/input2.json`;"); + } + + @Test + //map -> json. + public void testX2() throws Exception{ + test("select convert_to(x, 'JSON') from cp.`jsoninput/input2.json`;"); + } + + @Test + //Map (mapfield) -> json. + public void testX3() throws Exception{ + test("select convert_to(x['y'], 'JSON') from cp.`jsoninput/input2.json`;"); + } + + @Test + //float value -> json + public void testX4() throws Exception{ + test("select convert_to(`float`, 'JSON') from cp.`jsoninput/input2.json`;"); + } + + @Test + //integer value -> json + public void testX5() throws Exception{ + test("select convert_to(`integer`, 'JSON') from cp.`jsoninput/input2.json`;"); + } + + @Test + // repeated map -> json. + public void testX6() throws Exception{ + test("select convert_to(z, 'JSON') from cp.`jsoninput/input2.json`;"); + } + + @Test + //repeated list (Repeated BigInt) -> json + public void testX7() throws Exception{ + test("select convert_to(rl[1], 'JSON') from cp.`jsoninput/input2.json`;"); + } + + @Test + //repeated list (Repeated BigInt) -> json + public void testX8() throws Exception{ + test("select convert_to(rl[0][1], 'JSON') from cp.`jsoninput/input2.json`;"); + } + + @Test + //repeated list -> json + public void testX9() throws Exception{ + test("select convert_to(rl, 'JSON') from cp.`jsoninput/input2.json`;"); + } + + @Test + public void testY() throws Exception{ + test("select z[0] from cp.`jsoninput/input2.json`;"); + } + + @Test + public void testY2() throws Exception{ + test("select x from cp.`jsoninput/input2.json`;"); + } + + @Test + public void testY3() throws Exception{ + test("select x['y'] from cp.`jsoninput/input2.json`;"); + } + + @Test + public void testY6() throws Exception{ + test("select z from cp.`jsoninput/input2.json`;"); + } + + @Test + //repeated list (Repeated BigInt) + public void testZ() throws Exception{ + test("select rl[1] from cp.`jsoninput/input2.json`;"); + } + + @Test + //repeated list (Repeated BigInt ( BigInt) ) ) + public void testZ1() throws Exception{ + test("select rl[0][1] from cp.`jsoninput/input2.json`;"); + } + + @Test + //repeated list (Repeated BigInt ( BigInt) ) ). The first index is out of boundary + public void testZ2() throws Exception{ + test("select rl[1000][1] from cp.`jsoninput/input2.json`;"); + } + + @Test + //repeated list (Repeated BigInt ( BigInt) ) ). The second index is out of boundary + public void testZ3() throws Exception{ + test("select rl[0][1000] from cp.`jsoninput/input2.json`;"); + } + + @Test + //repeated map --> Json. It will go beyond the buffer of size 256 allocated in setup. + public void testA0() throws Exception{ + test(" select convert_to(types, 'JSON') from cp.`jsoninput/vvtypes.json`;"); + } + + @Test + //repeated map (map) --> Json. + public void testA1() throws Exception{ + test(" select convert_to(types[1], 'JSON') from cp.`jsoninput/vvtypes.json`;"); + } + + @Test + //repeated map (map (repeated map) ) --> Json. + public void testA2() throws Exception{ + test(" select convert_to(types[1]['minor'], 'JSON') from cp.`jsoninput/vvtypes.json`;"); + } + + @Test + //repeated map (map( repeated map (map (varchar)))) --> Json. + public void testA3() throws Exception{ + test(" select convert_to(types[1]['minor'][0]['valueHolder'], 'JSON') from cp.`jsoninput/vvtypes.json`;"); + } + + @Test + //repeated map (map) . + public void testB1() throws Exception{ + test(" select types[1] from cp.`jsoninput/vvtypes.json`;"); + } + + @Test + //repeated map (map (repeated map) ). + public void testB2() throws Exception{ + test(" select types[1]['minor'] from cp.`jsoninput/vvtypes.json`;"); + } + + @Test + //repeated map (map( repeated map (map (varchar)))). + public void testB3() throws Exception{ + test(" select types[1]['minor'][0]['valueHolder'] from cp.`jsoninput/vvtypes.json`;"); + } + +} diff --git a/exec/java-exec/src/test/resources/jsoninput/vvtypes.json b/exec/java-exec/src/test/resources/jsoninput/vvtypes.json new file mode 100644 index 000000000..60cd1ea1e --- /dev/null +++ b/exec/java-exec/src/test/resources/jsoninput/vvtypes.json @@ -0,0 +1,140 @@ +{ + modes: [ + {name: "Optional", prefix: "Nullable"}, + {name: "Required", prefix: ""}, + {name: "Repeated", prefix: "Repeated"} + ], + types: [ + { + major: "Fixed", + width: 1, + javaType: "byte", + boxedType: "Byte", + minor: [ + { class: "TinyInt", valueHolder: "IntHolder"}, + { class: "UInt1", valueHolder: "UInt1Holder"} + ] + }, + { + major: "Fixed", + width: 2, + javaType: "char", + boxedType: "Character", + minor: [ + { class: "UInt2", valueHolder: "UInt2Holder"} + ] + }, { + major: "Fixed", + width: 2, + javaType: "short", + boxedType: "Short", + minor: [ + { class: "SmallInt", valueHolder: "Int2Holder"} + ] + }, + { + major: "Fixed", + width: 4, + javaType: "int", + boxedType: "Integer", + minor: [ + { class: "Int", valueHolder: "IntHolder" }, + { class: "UInt4", valueHolder: "UInt4Holder" }, + { class: "Float4", javaType: "float" , boxedType: "Float" }, + { class: "Time", javaType: "int", friendlyType: "DateTime" }, + { class: "IntervalYear", javaType: "int", friendlyType: "Period" }, + { class: "Decimal9", maxPrecisionDigits: 9, friendlyType: "BigDecimal" } + ] + }, + { + major: "Fixed", + width: 8, + javaType: "long", + boxedType: "Long", + minor: [ + { class: "BigInt" }, + { class: "UInt8" }, + { class: "Float8", javaType: "double" , boxedType: "Double" }, + { class: "Date", javaType: "long", friendlyType: "DateTime" }, + { class: "TimeStamp", javaType: "long", friendlyType: "DateTime" }, + { class: "Decimal18", maxPrecisionDigits: 18, friendlyType: "BigDecimal" } + ] + }, + { + major: "Fixed", + width: 12, + javaType: "ByteBuf", + boxedType: "ByteBuf", + minor: [ + { class: "TimeStampTZ", milliSecondsSize: 8, friendlyType: "DateTime" }, + { class: "IntervalDay", milliSecondsOffset: 4, friendlyType: "Period" } + ] + }, + { + major: "Fixed", + width: 16, + javaType: "ByteBuf", + boxedType: "ByteBuf", + minor: [ + { class: "Interval", daysOffset: 4, milliSecondsOffset: 8, friendlyType: "Period" } + ] + }, + { + major: "Fixed", + width: 12, + javaType: "ByteBuf", + boxedType: "ByteBuf", + minor: [ + { class: "Decimal28Dense", maxPrecisionDigits: 28, nDecimalDigits: 3, friendlyType: "BigDecimal" } + ] + }, + { + major: "Fixed", + width: 16, + javaType: "ByteBuf", + boxedType: "ByteBuf", + + minor: [ + { class: "Decimal38Dense", maxPrecisionDigits: 38, nDecimalDigits: 4, friendlyType: "BigDecimal" } + ] + }, + { + major: "Fixed", + width: 24, + javaType: "ByteBuf", + boxedType: "ByteBuf", + minor: [ + { class: "Decimal38Sparse", maxPrecisionDigits: 38, nDecimalDigits: 6, friendlyType: "BigDecimal" } + ] + }, + { + major: "Fixed", + width: 20, + javaType: "ByteBuf", + boxedType: "ByteBuf", + minor: [ + { class: "Decimal28Sparse", maxPrecisionDigits: 28, nDecimalDigits: 5, friendlyType: "BigDecimal" } + ] + }, + { + major: "VarLen", + width: 4, + javaType: "int", + boxedType: "ByteBuf", + minor: [ + { class: "VarBinary" , friendlyType: "byte[]" }, + { class: "VarChar" , friendlyType: "Text" }, + { class: "Var16Char" , friendlyType: "String" } + ] + }, + { + major: "Bit", + width: 1, + javaType: "int", + boxedType: "ByteBuf", + minor: [ + { class: "Bit" , friendlyType: "Boolean" } + ] + } + ] +} |