DRILL-1109: Implement downward casting functions for decimal data type.
Project: http://git-wip-us.apache.org/repos/asf/incubator-drill/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-drill/commit/fed331b3 Tree: http://git-wip-us.apache.org/repos/asf/incubator-drill/tree/fed331b3 Diff: http://git-wip-us.apache.org/repos/asf/incubator-drill/diff/fed331b3 Branch: refs/heads/master Commit: fed331b393a90ad575a4ad3765e0981094f3c541 Parents: ae73875 Author: Mehant Baid <[email protected]> Authored: Sun Jul 6 21:40:16 2014 -0700 Committer: Jacques Nadeau <[email protected]> Committed: Mon Jul 7 14:50:32 2014 -0700 ---------------------------------------------------------------------- exec/java-exec/src/main/codegen/data/Casts.tdd | 12 +- .../templates/Decimal/CastDownwardDecimal.java | 176 +++++++++++++++++++ .../drill/jdbc/test/TestFunctionsQuery.java | 21 +++ 3 files changed, 208 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/fed331b3/exec/java-exec/src/main/codegen/data/Casts.tdd ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/codegen/data/Casts.tdd b/exec/java-exec/src/main/codegen/data/Casts.tdd index 2869bd0..2b76514 100644 --- a/exec/java-exec/src/main/codegen/data/Casts.tdd +++ b/exec/java-exec/src/main/codegen/data/Casts.tdd @@ -157,6 +157,16 @@ {from: "Decimal9", to: "VarChar", major: "DecimalSimpleVarChar", bufferSize: "11", javatype: "int"}, {from: "Decimal18", to: "VarChar", major: "DecimalSimpleVarChar", bufferSize: "20", javatype: "long"}, {from: "Decimal28Sparse", to: "VarChar", major: "DecimalComplexVarChar", bufferSize: "30", arraySize: "5"}, - {from: "Decimal38Sparse", to: "VarChar", major: "DecimalComplexVarChar", bufferSize: "40", arraySize: "6"} + {from: "Decimal38Sparse", to: "VarChar", major: "DecimalComplexVarChar", bufferSize: "40", arraySize: "6"}, + + {from: "Decimal18", to: "Decimal9", major: "DownwardDecimalSimpleDecimalSimple", javatype: "int"}, + + {from: "Decimal28Sparse", to: "Decimal18", major: "DownwardDecimalSimpleDecimalComplex", javatype: "long"}, + {from: "Decimal28Sparse", to: "Decimal9", major: "DownwardDecimalSimpleDecimalComplex", javatype: "int"}, + + {from: "Decimal38Sparse", to: "Decimal28Sparse", major: "DownwardDecimalComplexDecimalComplex", arraySize: "5"}, + {from: "Decimal38Sparse", to: "Decimal18", major: "DownwardDecimalComplexDecimalSimple", javatype: "long"}, + {from: "Decimal38Sparse", to: "Decimal9", major: "DownwardDecimalComplexDecimalSimple", javatype: "int"} + ] } http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/fed331b3/exec/java-exec/src/main/codegen/templates/Decimal/CastDownwardDecimal.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/codegen/templates/Decimal/CastDownwardDecimal.java b/exec/java-exec/src/main/codegen/templates/Decimal/CastDownwardDecimal.java new file mode 100644 index 0000000..516ec82 --- /dev/null +++ b/exec/java-exec/src/main/codegen/templates/Decimal/CastDownwardDecimal.java @@ -0,0 +1,176 @@ +/** + * 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 cast.types as type> + +<#if type.major == "DownwardDecimalSimpleDecimalSimple"> <#-- Cast function template for conversion from Decimal18, Decimal9 --> +<@pp.changeOutputFile name="/org/apache/drill/exec/expr/fn/impl/gcast/Cast${type.from}${type.to}.java" /> + +<#include "/@includes/license.ftl" /> + +package org.apache.drill.exec.expr.fn.impl.gcast; + +import org.apache.drill.exec.expr.DrillSimpleFunc; +import org.apache.drill.exec.expr.annotations.FunctionTemplate; +import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling; +import org.apache.drill.exec.expr.annotations.Output; +import org.apache.drill.exec.expr.annotations.Param; +import org.apache.drill.exec.expr.holders.*; +import org.apache.drill.exec.record.RecordBatch; +import org.apache.drill.common.util.DecimalUtility; +import org.apache.drill.exec.expr.annotations.Workspace; +import io.netty.buffer.ByteBuf; +import java.nio.ByteBuffer; + +@SuppressWarnings("unused") +@FunctionTemplate(name = "cast${type.to?upper_case}", scope = FunctionTemplate.FunctionScope.DECIMAL_CAST, nulls=NullHandling.NULL_IF_NULL) +public class Cast${type.from}${type.to} implements DrillSimpleFunc { + + @Param ${type.from}Holder in; + @Param BigIntHolder precision; + @Param BigIntHolder scale; + @Output ${type.to}Holder out; + + public void setup(RecordBatch incoming) { + } + + public void eval() { + out.scale = (int) scale.value; + out.precision = (int) precision.value; + out.value = (int) in.value; + // Check if we need to truncate or round up + if (out.scale > in.scale) { + out.value *= (int) org.apache.drill.common.util.DecimalUtility.getPowerOfTen(out.scale - in.scale); + } else if (out.scale < in.scale) { + // need to round up since we are truncating fractional part + int scaleFactor = (int) (org.apache.drill.common.util.DecimalUtility.getPowerOfTen((int) in.scale)); + int newScaleFactor = (int) (org.apache.drill.common.util.DecimalUtility.getPowerOfTen((int) scale.value)); + int truncScaleFactor = (int) (org.apache.drill.common.util.DecimalUtility.getPowerOfTen( (int) (Math.abs(in.scale - scale.value)))); + int truncFactor = (int) (in.scale - scale.value); + + // Assign the integer part + out.value = (int) (in.value / scaleFactor); + + // Get the fractional part + int fractionalPart = (int) (in.value % scaleFactor); + + // From the entire fractional part extract the digits upto which rounding is needed + int newFractionalPart = (int) (org.apache.drill.common.util.DecimalUtility.adjustScaleDivide(fractionalPart, truncFactor)); + int truncatedFraction = fractionalPart % truncScaleFactor; + + // Get the truncated fractional part and extract the first digit to see if we need to add 1 + int digit = Math.abs((int) org.apache.drill.common.util.DecimalUtility.adjustScaleDivide(truncatedFraction, truncFactor - 1)); + + if (digit > 4) { + if (in.value > 0) { + newFractionalPart++; + } else if (in.value < 0) { + newFractionalPart--; + } + } + out.value = (int) ((out.value * newScaleFactor) + newFractionalPart); + } + } +} +<#elseif type.major == "DownwardDecimalComplexDecimalSimple"> <#-- Cast function template for conversion from Decimal28/Decimal9 to Decimal18/Decimal9 --> +<@pp.changeOutputFile name="/org/apache/drill/exec/expr/fn/impl/gcast/Cast${type.from}${type.to}.java" /> + +<#include "/@includes/license.ftl" /> + +package org.apache.drill.exec.expr.fn.impl.gcast; + +import org.apache.drill.exec.expr.DrillSimpleFunc; +import org.apache.drill.exec.expr.annotations.FunctionTemplate; +import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling; +import org.apache.drill.exec.expr.annotations.Output; +import org.apache.drill.exec.expr.annotations.Param; +import org.apache.drill.exec.expr.holders.*; +import org.apache.drill.exec.record.RecordBatch; +import org.apache.drill.common.util.DecimalUtility; +import org.apache.drill.exec.expr.annotations.Workspace; +import io.netty.buffer.ByteBuf; +import java.nio.ByteBuffer; + +@SuppressWarnings("unused") +@FunctionTemplate(name = "cast${type.to?upper_case}", scope = FunctionTemplate.FunctionScope.DECIMAL_CAST, nulls=NullHandling.NULL_IF_NULL) +public class Cast${type.from}${type.to} implements DrillSimpleFunc { + + @Param ${type.from}Holder in; + @Param BigIntHolder precision; + @Param BigIntHolder scale; + @Output ${type.to}Holder out; + + public void setup(RecordBatch incoming) { + } + + public void eval() { + java.math.BigDecimal temp = org.apache.drill.common.util.DecimalUtility.getBigDecimalFromSparse(in.buffer, in.start, in.nDecimalDigits, in.scale); + temp = temp.setScale((int) scale.value, java.math.BigDecimal.ROUND_HALF_UP); + out.value = temp.unscaledValue().${type.javatype}Value(); + out.precision = (int) precision.value; + out.scale = (int) scale.value; + } +} +<#elseif type.major == "DownwardDecimalComplexDecimalComplex"> <#-- Cast function template for conversion from Decimal28/Decimal9 to Decimal18/Decimal9 --> +<@pp.changeOutputFile name="/org/apache/drill/exec/expr/fn/impl/gcast/Cast${type.from}${type.to}.java" /> + +<#include "/@includes/license.ftl" /> + +package org.apache.drill.exec.expr.fn.impl.gcast; + +import org.apache.drill.exec.expr.DrillSimpleFunc; +import org.apache.drill.exec.expr.annotations.FunctionTemplate; +import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling; +import org.apache.drill.exec.expr.annotations.Output; +import org.apache.drill.exec.expr.annotations.Param; +import org.apache.drill.exec.expr.holders.*; +import org.apache.drill.exec.record.RecordBatch; +import org.apache.drill.common.util.DecimalUtility; +import org.apache.drill.exec.expr.annotations.Workspace; +import io.netty.buffer.ByteBuf; +import java.nio.ByteBuffer; + +@SuppressWarnings("unused") +@FunctionTemplate(name = "cast${type.to?upper_case}", scope = FunctionTemplate.FunctionScope.DECIMAL_CAST, nulls=NullHandling.NULL_IF_NULL) +public class Cast${type.from}${type.to} implements DrillSimpleFunc { + + @Param ${type.from}Holder in; + @Param BigIntHolder precision; + @Param BigIntHolder scale; + @Workspace ByteBuf buffer; + @Output ${type.to}Holder out; + + public void setup(RecordBatch incoming) { + int size = (${type.arraySize} * (org.apache.drill.common.util.DecimalUtility.integerSize)); + buffer = io.netty.buffer.Unpooled.wrappedBuffer(new byte[size]); + buffer = new io.netty.buffer.SwappedByteBuf(buffer); + } + + public void eval() { + java.math.BigDecimal temp = org.apache.drill.common.util.DecimalUtility.getBigDecimalFromSparse(in.buffer, in.start, in.nDecimalDigits, in.scale); + temp = temp.setScale((int) scale.value, java.math.BigDecimal.ROUND_HALF_UP); + out.precision = (int) precision.value; + out.scale = (int) scale.value; + out.buffer = buffer; + out.start = 0; + org.apache.drill.common.util.DecimalUtility.getSparseFromBigDecimal(temp, out.buffer, out.start, out.scale, out.precision, out.nDecimalDigits); + } +} +</#if> <#-- type.major --> +</#list> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/fed331b3/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java ---------------------------------------------------------------------- diff --git a/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java index 99c85e5..3ec79c8 100644 --- a/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java +++ b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestFunctionsQuery.java @@ -701,4 +701,25 @@ public class TestFunctionsQuery { .returns("TRIM_STR=Sheri; "+ "SUB_STR=heri\n"); } + @Test + public void testDecimalDownwardCast() throws Exception { + String query = "select cast((cast('12345.6789' as decimal(18, 4))) as decimal(9, 4)) as DEC18_DEC9_1, " + + "cast((cast('12345.6789' as decimal(18, 4))) as decimal(9, 2)) as DEC18_DEC9_2, " + + "cast((cast('-12345.6789' as decimal(18, 4))) as decimal(9, 0)) as DEC18_DEC9_3, " + + "cast((cast('999999999.6789' as decimal(38, 4))) as decimal(9, 0)) as DEC38_DEC19_1, " + + "cast((cast('-999999999999999.6789' as decimal(38, 4))) as decimal(18, 2)) as DEC38_DEC18_1, " + + "cast((cast('-999999999999999.6789' as decimal(38, 4))) as decimal(18, 0)) as DEC38_DEC18_2, " + + "cast((cast('100000000999999999.6789' as decimal(38, 4))) as decimal(28, 0)) as DEC38_DEC28_1 " + + "from cp.`employee.json` where employee_id = 1"; + + JdbcAssert.withNoDefaultSchema() + .sql(query) + .returns("DEC18_DEC9_1=12345.6789; "+ + "DEC18_DEC9_2=12345.68; " + + "DEC18_DEC9_3=-12346; " + + "DEC38_DEC19_1=1000000000; " + + "DEC38_DEC18_1=-999999999999999.68; " + + "DEC38_DEC18_2=-1000000000000000; " + + "DEC38_DEC28_1=100000001000000000\n"); + } }
