diff --git a/ant/src/org/apache/hadoop/hive/ant/GenVectorCode.java b/ant/src/org/apache/hadoop/hive/ant/GenVectorCode.java index 71388f5..1b76fc9 100644 --- a/ant/src/org/apache/hadoop/hive/ant/GenVectorCode.java +++ b/ant/src/org/apache/hadoop/hive/ant/GenVectorCode.java @@ -119,6 +119,15 @@ {"ColumnArithmeticColumnDecimal", "Subtract"}, {"ColumnArithmeticColumnDecimal", "Multiply"}, + {"ColumnDivideScalarDecimal", "Divide"}, + {"ColumnDivideScalarDecimal", "Modulo"}, + + {"ScalarDivideColumnDecimal", "Divide"}, + {"ScalarDivideColumnDecimal", "Modulo"}, + + {"ColumnDivideColumnDecimal", "Divide"}, + {"ColumnDivideColumnDecimal", "Modulo"}, + {"ColumnCompareScalar", "Equal", "long", "double", "=="}, {"ColumnCompareScalar", "Equal", "double", "double", "=="}, {"ColumnCompareScalar", "NotEqual", "long", "double", "!="}, @@ -578,6 +587,12 @@ private void generate() throws Exception { generateScalarArithmeticColumnDecimal(tdesc); } else if (tdesc[0].equals("ColumnArithmeticColumnDecimal")) { generateColumnArithmeticColumnDecimal(tdesc); + } else if (tdesc[0].equals("ColumnDivideScalarDecimal")) { + generateColumnDivideScalarDecimal(tdesc); + } else if (tdesc[0].equals("ScalarDivideColumnDecimal")) { + generateScalarDivideColumnDecimal(tdesc); + } else if (tdesc[0].equals("ColumnDivideColumnDecimal")) { + generateColumnDivideColumnDecimal(tdesc); } else if (tdesc[0].equals("ColumnCompareScalar")) { generateColumnCompareScalar(tdesc); } else if (tdesc[0].equals("ScalarCompareColumn")) { @@ -1203,6 +1218,48 @@ private void generateColumnArithmeticColumnDecimal(String[] tdesc) throws IOExce className, templateString); } + private void generateColumnDivideScalarDecimal(String[] tdesc) throws IOException { + String operatorName = tdesc[1]; + String className = "DecimalCol" + getInitialCapWord(operatorName) + "DecimalScalar"; + + // Read the template into a string; + File templateFile = new File(joinPath(this.expressionTemplateDirectory, tdesc[0] + ".txt")); + String templateString = readFile(templateFile); + templateString = templateString.replaceAll("", className); + templateString = templateString.replaceAll("", operatorName.toLowerCase()); + + writeFile(templateFile.lastModified(), expressionOutputDirectory, expressionClassesDirectory, + className, templateString); + } + + private void generateScalarDivideColumnDecimal(String[] tdesc) throws IOException { + String operatorName = tdesc[1]; + String className = "DecimalScalar" + getInitialCapWord(operatorName) + "DecimalColumn"; + + // Read the template into a string; + File templateFile = new File(joinPath(this.expressionTemplateDirectory, tdesc[0] + ".txt")); + String templateString = readFile(templateFile); + templateString = templateString.replaceAll("", className); + templateString = templateString.replaceAll("", operatorName.toLowerCase()); + + writeFile(templateFile.lastModified(), expressionOutputDirectory, expressionClassesDirectory, + className, templateString); + } + + private void generateColumnDivideColumnDecimal(String[] tdesc) throws IOException { + String operatorName = tdesc[1]; + String className = "DecimalCol" + getInitialCapWord(operatorName) + "DecimalColumn"; + + // Read the template into a string; + File templateFile = new File(joinPath(this.expressionTemplateDirectory, tdesc[0] + ".txt")); + String templateString = readFile(templateFile); + templateString = templateString.replaceAll("", className); + templateString = templateString.replaceAll("", operatorName.toLowerCase()); + + writeFile(templateFile.lastModified(), expressionOutputDirectory, expressionClassesDirectory, + className, templateString); + } + private void generateScalarArithmeticColumn(String[] tdesc) throws IOException { String operatorName = tdesc[1]; String operandType1 = tdesc[2]; @@ -1294,6 +1351,14 @@ static String getCamelCaseType(String type) { } } + /** + * Return the argument with the first letter capitalized + */ + private static String getInitialCapWord(String word) { + String firstLetterAsCap = word.substring(0, 1).toUpperCase(); + return firstLetterAsCap + word.substring(1); + } + private String getArithmeticReturnType(String operandType1, String operandType2) { if (operandType1.equals("double") || diff --git a/common/src/java/org/apache/hadoop/hive/common/type/Decimal128.java b/common/src/java/org/apache/hadoop/hive/common/type/Decimal128.java index 15025cf..e5d536d 100644 --- a/common/src/java/org/apache/hadoop/hive/common/type/Decimal128.java +++ b/common/src/java/org/apache/hadoop/hive/common/type/Decimal128.java @@ -1194,6 +1194,42 @@ public void divideDestructive(Decimal128 right, short newScale) { this.unscaledValue.throwIfExceedsTenToThirtyEight(); } + /** + * Performs decimal modulo + *

+ * The definition of modulo (x % p) is: + * x - IntegerPart(x / p, resultScale) * p + *

+ * + * @left + * is x + * @right + * is p + * @result + * receives the result + * @scratch + * scratch space to avoid need to create a new object + * @scale + * scale of result + */ + public static void modulo(Decimal128 left, Decimal128 right, Decimal128 result, + short scale) { + + // set result to x / p (the quotient) + Decimal128.divide(left, right, result, scale); + + // take integer part of it + result.zeroFractionPart(); + + // multiply by p + result.multiplyDestructive(right, scale); + + // negate it + result.negateDestructive(); + + // add x to it + result.addDestructive(left, scale); + } /** * Makes this {@code Decimal128} a positive number. Unlike @@ -1635,4 +1671,35 @@ public String toString() { public void setNullDataValue() { unscaledValue.update(1, 0, 0, 0); } + + /** + * Zero the fractional part of value. + * + * Argument scratch is needed to hold unused remainder output, to avoid need to + * create a new object. + */ + public void zeroFractionPart() { + short placesToRemove = this.getScale(); + + // If there's no fraction part, return immediately to avoid the cost of a divide. + if (placesToRemove == 0) { + return; + } + + /* Divide by a power of 10 equal to 10**scale to logically shift the digits + * places right by "scale" positions to eliminate them. + */ + UnsignedInt128 powerTenDivisor = SqlMathUtil.POWER_TENS_INT128[placesToRemove]; + + /* A scratch variable is created here. This could be optimized in the future + * by perhaps using thread-local storage to allocate this scratch field. + */ + UnsignedInt128 scratch = new UnsignedInt128(); + this.getUnscaledValue().divideDestructive(powerTenDivisor, scratch); + + /* Multiply by the same power of ten to shift the decimal point back to + * the original place. Places to the right of the decimal will be zero. + */ + this.getUnscaledValue().scaleUpTenDestructive(placesToRemove); + } } diff --git a/ql/src/gen/vectorization/ExpressionTemplates/ColumnArithmeticScalarDecimal.txt b/ql/src/gen/vectorization/ExpressionTemplates/ColumnArithmeticScalarDecimal.txt index c0bdd58..99366ca 100644 --- a/ql/src/gen/vectorization/ExpressionTemplates/ColumnArithmeticScalarDecimal.txt +++ b/ql/src/gen/vectorization/ExpressionTemplates/ColumnArithmeticScalarDecimal.txt @@ -117,7 +117,10 @@ public class extends VectorExpression { } } - NullUtil.setNullDataEntriesDecimal(outputColVector, batch.selectedInUse, sel, n); + /* + * Null data entries are not set to a special non-zero value because all null math operations + * are checked, meaning that a zero-divide always results in a null result. + */ } @Override diff --git a/ql/src/gen/vectorization/ExpressionTemplates/ColumnDivideColumnDecimal.txt b/ql/src/gen/vectorization/ExpressionTemplates/ColumnDivideColumnDecimal.txt new file mode 100644 index 0000000..55fdb0f --- /dev/null +++ b/ql/src/gen/vectorization/ExpressionTemplates/ColumnDivideColumnDecimal.txt @@ -0,0 +1,177 @@ +/** + * 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.hadoop.hive.ql.exec.vector.expressions.gen; + +import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpression; +import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.expressions.NullUtil; +import org.apache.hadoop.hive.ql.exec.vector.expressions.DecimalUtil; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.common.type.Decimal128; + +/** + * Generated from template ColumnArithmeticColumnDecimal.txt, which covers binary arithmetic + * expressions between a column and a scalar. + */ +public class extends VectorExpression { + + private static final long serialVersionUID = 1L; + + private int colNum1; + private int colNum2; + private int outputColumn; + + public (int colNum1, int colNum2, int outputColumn) { + this.colNum1 = colNum1; + this.colNum2 = colNum2; + this.outputColumn = outputColumn; + } + + public () { + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalColumnVector inputColVector1 = (DecimalColumnVector) batch.cols[colNum1]; + DecimalColumnVector inputColVector2 = (DecimalColumnVector) batch.cols[colNum2]; + DecimalColumnVector outputColVector = (DecimalColumnVector) batch.cols[outputColumn]; + int[] sel = batch.selected; + int n = batch.size; + Decimal128[] vector1 = inputColVector1.vector; + Decimal128[] vector2 = inputColVector2.vector; + + // return immediately if batch is empty + if (n == 0) { + return; + } + + outputColVector.isRepeating = + inputColVector1.isRepeating && inputColVector2.isRepeating + || inputColVector1.isRepeating && !inputColVector1.noNulls && inputColVector1.isNull[0] + || inputColVector2.isRepeating && !inputColVector2.noNulls && inputColVector2.isNull[0]; + + if (inputColVector1.noNulls && inputColVector2.noNulls) { + + /* Initialize output vector NULL values to false. This is necessary + * since the decimal operation may produce a NULL result even for + * a non-null input vector value, and convert the output vector + * to have noNulls = false; + */ + NullUtil.initOutputNullsToFalse(outputColVector, + inputColVector1.isRepeating && inputColVector2.isRepeating, + batch.selectedInUse, sel, n); + } + + // Handle nulls first + NullUtil.propagateNullsColCol( + inputColVector1, inputColVector2, outputColVector, sel, n, batch.selectedInUse); + + /* Disregard nulls for processing. In other words, + * the arithmetic operation is performed even if one or + * more inputs are null. This is to improve speed by avoiding + * conditional checks in the inner loop. + */ + if (inputColVector1.isRepeating && inputColVector2.isRepeating) { + DecimalUtil.Checked(0, vector1[0], vector2[0], outputColVector); + } else if (inputColVector1.isRepeating) { + if (batch.selectedInUse) { + for(int j = 0; j != n; j++) { + int i = sel[j]; + DecimalUtil.Checked(i, vector1[0], vector2[i], outputColVector); + } + } else { + for(int i = 0; i != n; i++) { + DecimalUtil.Checked(i, vector1[0], vector2[i], outputColVector); + } + } + } else if (inputColVector2.isRepeating) { + if (batch.selectedInUse) { + for(int j = 0; j != n; j++) { + int i = sel[j]; + DecimalUtil.Checked(i, vector1[i], vector2[0], outputColVector); + } + } else { + for(int i = 0; i != n; i++) { + DecimalUtil.Checked(i, vector1[i], vector2[0], outputColVector); + } + } + } else { + if (batch.selectedInUse) { + for(int j = 0; j != n; j++) { + int i = sel[j]; + DecimalUtil.Checked(i, vector1[i], vector2[i], outputColVector); + } + } else { + for(int i = 0; i != n; i++) { + DecimalUtil.Checked(i, vector1[i], vector2[i], outputColVector); + } + } + } + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + @Override + public String getOutputType() { + return "decimal"; + } + + public int getColNum1() { + return colNum1; + } + + public void setColNum1(int colNum1) { + this.colNum1 = colNum1; + } + + public int getColNum2() { + return colNum2; + } + + public void setColNum2(int colNum2) { + this.colNum2 = colNum2; + } + + public void setOutputColumn(int outputColumn) { + this.outputColumn = outputColumn; + } + + @Override + public VectorExpressionDescriptor.Descriptor getDescriptor() { + return (new VectorExpressionDescriptor.Builder()) + .setMode( + VectorExpressionDescriptor.Mode.PROJECTION) + .setNumArguments(2) + .setArgumentTypes( + VectorExpressionDescriptor.ArgumentType.getType("decimal"), + VectorExpressionDescriptor.ArgumentType.getType("decimal")) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.COLUMN).build(); + } +} diff --git a/ql/src/gen/vectorization/ExpressionTemplates/ColumnDivideScalarDecimal.txt b/ql/src/gen/vectorization/ExpressionTemplates/ColumnDivideScalarDecimal.txt new file mode 100644 index 0000000..703013e --- /dev/null +++ b/ql/src/gen/vectorization/ExpressionTemplates/ColumnDivideScalarDecimal.txt @@ -0,0 +1,177 @@ +/** + * 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.hadoop.hive.ql.exec.vector.expressions.gen; + +import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpression; +import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.expressions.NullUtil; +import org.apache.hadoop.hive.ql.exec.vector.expressions.DecimalUtil; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.common.type.Decimal128; + +/** + * Generated from template ColumnDivideScalarDecimal.txt, which covers binary arithmetic + * expressions between a column and a scalar. + */ +public class extends VectorExpression { + + private static final long serialVersionUID = 1L; + + private int colNum; + private Decimal128 value; + private int outputColumn; + private transient Decimal128 zero; // to hold constant 0 for later use + + public (int colNum, Decimal128 value, int outputColumn) { + this.colNum = colNum; + this.value = value; + this.outputColumn = outputColumn; + } + + public () { + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalColumnVector inputColVector = (DecimalColumnVector) batch.cols[colNum]; + DecimalColumnVector outputColVector = (DecimalColumnVector) batch.cols[outputColumn]; + int[] sel = batch.selected; + boolean[] inputIsNull = inputColVector.isNull; + boolean[] outputIsNull = outputColVector.isNull; + outputColVector.noNulls = inputColVector.noNulls; + outputColVector.isRepeating = inputColVector.isRepeating; + int n = batch.size; + Decimal128[] vector = inputColVector.vector; + Decimal128[] outputVector = outputColVector.vector; + + // Initialize local variable to use as 0 value on first use. + if (zero == null) { + this.zero = new Decimal128(0, inputColVector.scale); + } + + // return immediately if batch is empty + if (n == 0) { + return; + } + + if (inputColVector.noNulls) { + + /* Initialize output vector NULL values to false. This is necessary + * since the decimal operation may produce a NULL result even for + * a non-null input vector value, and convert the output vector + * to have noNulls = false; + */ + NullUtil.initOutputNullsToFalse(outputColVector, inputColVector.isRepeating, + batch.selectedInUse, sel, n); + } + + + if (value.compareTo(zero) == 0) { + + // Denominator is zero, convert the batch to nulls + outputColVector.noNulls = false; + outputColVector.isRepeating = true; + outputIsNull[0] = true; + } else if (inputColVector.isRepeating) { + DecimalUtil.Checked(0, vector[0], value, outputColVector); + + // Even if there are no nulls, we always copy over entry 0. Simplifies code. + outputIsNull[0] = inputIsNull[0]; + } else if (inputColVector.noNulls) { + if (batch.selectedInUse) { + for(int j = 0; j != n; j++) { + int i = sel[j]; + DecimalUtil.Checked(i, vector[i], value, outputColVector); + } + } else { + for(int i = 0; i != n; i++) { + DecimalUtil.Checked(i, vector[i], value, outputColVector); + } + } + } else /* there are nulls */ { + if (batch.selectedInUse) { + for(int j = 0; j != n; j++) { + int i = sel[j]; + DecimalUtil.Checked(i, vector[i], value, outputColVector); + outputIsNull[i] = inputIsNull[i]; + } + } else { + for(int i = 0; i != n; i++) { + DecimalUtil.Checked(i, vector[i], value, outputColVector); + } + System.arraycopy(inputIsNull, 0, outputIsNull, 0, n); + } + } + + /* + * Null data entries are not set to a special non-zero value because all null math operations + * are checked, meaning that a zero-divide always results in a null result. + */ + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + @Override + public String getOutputType() { + return "decimal"; + } + + public int getColNum() { + return colNum; + } + + public void setColNum(int colNum) { + this.colNum = colNum; + } + + public Decimal128 getValue() { + return value; + } + + public void setValue(Decimal128 value) { + this.value = value; + } + + public void setOutputColumn(int outputColumn) { + this.outputColumn = outputColumn; + } + + @Override + public VectorExpressionDescriptor.Descriptor getDescriptor() { + return (new VectorExpressionDescriptor.Builder()) + .setMode( + VectorExpressionDescriptor.Mode.PROJECTION) + .setNumArguments(2) + .setArgumentTypes( + VectorExpressionDescriptor.ArgumentType.getType("decimal"), + VectorExpressionDescriptor.ArgumentType.getType("decimal")) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.SCALAR).build(); + } +} diff --git a/ql/src/gen/vectorization/ExpressionTemplates/FilterDecimalColumnCompareColumn.txt b/ql/src/gen/vectorization/ExpressionTemplates/FilterDecimalColumnCompareColumn.txt index e0a9ac2..8001fb7 100644 --- a/ql/src/gen/vectorization/ExpressionTemplates/FilterDecimalColumnCompareColumn.txt +++ b/ql/src/gen/vectorization/ExpressionTemplates/FilterDecimalColumnCompareColumn.txt @@ -140,7 +140,7 @@ public class extends VectorExpression { batch.selectedInUse = true; } } - + // handle case where only input 2 has nulls } else if (inputColVector1.noNulls) { if (inputColVector1.isRepeating && inputColVector2.isRepeating) { diff --git a/ql/src/gen/vectorization/ExpressionTemplates/ScalarDivideColumnDecimal.txt b/ql/src/gen/vectorization/ExpressionTemplates/ScalarDivideColumnDecimal.txt new file mode 100644 index 0000000..3f409dc --- /dev/null +++ b/ql/src/gen/vectorization/ExpressionTemplates/ScalarDivideColumnDecimal.txt @@ -0,0 +1,164 @@ +/** + * 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.hadoop.hive.ql.exec.vector.expressions.gen; + +import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpression; +import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; +import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.expressions.NullUtil; +import org.apache.hadoop.hive.ql.exec.vector.expressions.DecimalUtil; +import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor; +import org.apache.hadoop.hive.common.type.Decimal128; + +/** + * Generated from template ScalarDivideColumnDecimal.txt, which covers binary arithmetic + * expressions between a scalar and a column. + */ +public class extends VectorExpression { + + private static final long serialVersionUID = 1L; + + private int colNum; + private Decimal128 value; + private int outputColumn; + + public (Decimal128 value, int colNum, int outputColumn) { + this.colNum = colNum; + this.value = value; + this.outputColumn = outputColumn; + } + + public () { + } + + @Override + public void evaluate(VectorizedRowBatch batch) { + + if (childExpressions != null) { + super.evaluateChildren(batch); + } + + DecimalColumnVector inputColVector = (DecimalColumnVector) batch.cols[colNum]; + DecimalColumnVector outputColVector = (DecimalColumnVector) batch.cols[outputColumn]; + int[] sel = batch.selected; + boolean[] inputIsNull = inputColVector.isNull; + boolean[] outputIsNull = outputColVector.isNull; + outputColVector.noNulls = inputColVector.noNulls; + outputColVector.isRepeating = inputColVector.isRepeating; + int n = batch.size; + Decimal128[] vector = inputColVector.vector; + Decimal128[] outputVector = outputColVector.vector; + + // return immediately if batch is empty + if (n == 0) { + return; + } + + if (inputColVector.noNulls) { + + /* Initialize output vector NULL values to false. This is necessary + * since the decimal operation may produce a NULL result even for + * a non-null input vector value, and convert the output vector + * to have noNulls = false; + */ + NullUtil.initOutputNullsToFalse(outputColVector, inputColVector.isRepeating, + batch.selectedInUse, sel, n); + } + + if (inputColVector.isRepeating) { + DecimalUtil.Checked(0, value, vector[0], outputColVector); + + // Even if there are no nulls, we always copy over entry 0. Simplifies code. + outputIsNull[0] = inputIsNull[0]; + } else if (inputColVector.noNulls) { + if (batch.selectedInUse) { + for(int j = 0; j != n; j++) { + int i = sel[j]; + DecimalUtil.Checked(i, value, vector[i], outputColVector); + } + } else { + for(int i = 0; i != n; i++) { + DecimalUtil.Checked(i, value, vector[i], outputColVector); + } + } + } else /* there are nulls */ { + if (batch.selectedInUse) { + for(int j = 0; j != n; j++) { + int i = sel[j]; + DecimalUtil.Checked(i, value, vector[i], outputColVector); + outputIsNull[i] = inputIsNull[i]; + } + } else { + for(int i = 0; i != n; i++) { + DecimalUtil.Checked(i, value, vector[i], outputColVector); + } + System.arraycopy(inputIsNull, 0, outputIsNull, 0, n); + } + } + + /* + * Null data entries are not set to a special non-zero value because all null math operations + * are checked, meaning that a zero-divide always results in a null result anyway. + */ + } + + @Override + public int getOutputColumn() { + return outputColumn; + } + + @Override + public String getOutputType() { + return "decimal"; + } + + public int getColNum() { + return colNum; + } + + public void setColNum(int colNum) { + this.colNum = colNum; + } + + public Decimal128 getValue() { + return value; + } + + public void setValue(Decimal128 value) { + this.value = value; + } + + public void setOutputColumn(int outputColumn) { + this.outputColumn = outputColumn; + } + + @Override + public VectorExpressionDescriptor.Descriptor getDescriptor() { + return (new VectorExpressionDescriptor.Builder()) + .setMode( + VectorExpressionDescriptor.Mode.PROJECTION) + .setNumArguments(2) + .setArgumentTypes( + VectorExpressionDescriptor.ArgumentType.getType("decimal"), + VectorExpressionDescriptor.ArgumentType.getType("decimal")) + .setInputExpressionTypes( + VectorExpressionDescriptor.InputExpressionType.COLUMN, + VectorExpressionDescriptor.InputExpressionType.SCALAR).build(); + } +} diff --git a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalColAddDecimalColumn.java b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalColAddDecimalColumn.java deleted file mode 100644 index 02dc5a4..0000000 --- a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalColAddDecimalColumn.java +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.hive.ql.exec.vector.expressions; - -import org.apache.hadoop.hive.common.type.Decimal128; -import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpression; -import org.apache.hadoop.hive.ql.exec.vector.expressions.NullUtil; -import org.apache.hadoop.hive.ql.exec.vector.*; - -/** - * Generated from template ColumnArithmeticColumn.txt, which covers binary arithmetic - * expressions between columns. - */ -public class DecimalColAddDecimalColumn extends VectorExpression { - - private static final long serialVersionUID = 1L; - - private int colNum1; - private int colNum2; - private int outputColumn; - - public DecimalColAddDecimalColumn(int colNum1, int colNum2, int outputColumn) { - this.colNum1 = colNum1; - this.colNum2 = colNum2; - this.outputColumn = outputColumn; - } - - public DecimalColAddDecimalColumn() { - } - - @Override - public void evaluate(VectorizedRowBatch batch) { - - if (childExpressions != null) { - super.evaluateChildren(batch); - } - - DecimalColumnVector inputColVector1 = (DecimalColumnVector) batch.cols[colNum1]; - DecimalColumnVector inputColVector2 = (DecimalColumnVector) batch.cols[colNum2]; - DecimalColumnVector outputColVector = (DecimalColumnVector) batch.cols[outputColumn]; - int[] sel = batch.selected; - int n = batch.size; - Decimal128[] vector1 = inputColVector1.vector; - Decimal128[] vector2 = inputColVector2.vector; - Decimal128[] outputVector = outputColVector.vector; - - // return immediately if batch is empty - if (n == 0) { - return; - } - - outputColVector.isRepeating = - inputColVector1.isRepeating && inputColVector2.isRepeating - || inputColVector1.isRepeating && !inputColVector1.noNulls && inputColVector1.isNull[0] - || inputColVector2.isRepeating && !inputColVector2.noNulls && inputColVector2.isNull[0]; - - // Handle nulls first - NullUtil.propagateNullsColCol( - inputColVector1, inputColVector2, outputColVector, sel, n, batch.selectedInUse); - - /* Disregard nulls for processing. In other words, - * the arithmetic operation is performed even if one or - * more inputs are null. This is to improve speed by avoiding - * conditional checks in the inner loop. - */ - if (inputColVector1.isRepeating && inputColVector2.isRepeating) { - addChecked(0, vector1[0], vector2[0], outputColVector); - } else if (inputColVector1.isRepeating) { - if (batch.selectedInUse) { - for(int j = 0; j != n; j++) { - int i = sel[j]; - addChecked(i, vector1[0], vector2[i], outputColVector); - } - } else { - for(int i = 0; i != n; i++) { - addChecked(i, vector1[0], vector2[i], outputColVector); - } - } - } else if (inputColVector2.isRepeating) { - if (batch.selectedInUse) { - for(int j = 0; j != n; j++) { - int i = sel[j]; - addChecked(i, vector1[i], vector2[0], outputColVector); - } - } else { - for(int i = 0; i != n; i++) { - addChecked(i, vector1[i], vector2[0], outputColVector); - } - } - } else { - if (batch.selectedInUse) { - for(int j = 0; j != n; j++) { - int i = sel[j]; - addChecked(i, vector1[i], vector2[i], outputColVector); - } - } else { - for(int i = 0; i != n; i++) { - addChecked(i, vector1[i], vector2[i], outputColVector); - } - } - } - - /* For the case when the output can have null values, follow - * the convention that the data values must be 1 for long and - * NaN for double. This is to prevent possible later zero-divide errors - * in complex arithmetic expressions like col2 / (col1 - 1) - * in the case when some col1 entries are null. - */ - NullUtil.setNullDataEntriesDecimal(outputColVector, batch.selectedInUse, sel, n); - } - - // Addition with overflow check. Overflow produces NULL output. - private static void addChecked(int i, Decimal128 left, Decimal128 right, - DecimalColumnVector outputColVector) { - try { - Decimal128.add(left, right, outputColVector.vector[i], outputColVector.scale); - outputColVector.vector[i].checkPrecisionOverflow(outputColVector.precision); - } catch (ArithmeticException e) { // catch on overflow - outputColVector.noNulls = false; - outputColVector.isNull[i] = true; - } - } - - @Override - public int getOutputColumn() { - return outputColumn; - } - - @Override - public String getOutputType() { - return "long"; - } - - public int getColNum1() { - return colNum1; - } - - public void setColNum1(int colNum1) { - this.colNum1 = colNum1; - } - - public int getColNum2() { - return colNum2; - } - - public void setColNum2(int colNum2) { - this.colNum2 = colNum2; - } - - public void setOutputColumn(int outputColumn) { - this.outputColumn = outputColumn; - } - - @Override - public VectorExpressionDescriptor.Descriptor getDescriptor() { - return (new VectorExpressionDescriptor.Builder()) - .setMode( - VectorExpressionDescriptor.Mode.PROJECTION) - .setNumArguments(2) - .setArgumentTypes( - VectorExpressionDescriptor.ArgumentType.getType("decimal"), - VectorExpressionDescriptor.ArgumentType.getType("decimal")) - .setInputExpressionTypes( - VectorExpressionDescriptor.InputExpressionType.COLUMN, - VectorExpressionDescriptor.InputExpressionType.COLUMN).build(); - } -} diff --git a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalUtil.java b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalUtil.java index 48ec13a..589450f 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalUtil.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/DecimalUtil.java @@ -19,6 +19,8 @@ package org.apache.hadoop.hive.ql.exec.vector.expressions; import org.apache.hadoop.hive.common.type.Decimal128; +import org.apache.hadoop.hive.common.type.SqlMathUtil; +import org.apache.hadoop.hive.common.type.UnsignedInt128; import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; /** @@ -73,4 +75,16 @@ public static void divideChecked(int i, Decimal128 left, Decimal128 right, outputColVector.isNull[i] = true; } } + + // Modulo operator with overflow/zero-divide check. + public static void moduloChecked(int i, Decimal128 left, Decimal128 right, + DecimalColumnVector outputColVector) { + try { + Decimal128.modulo(left, right, outputColVector.vector[i], outputColVector.scale); + outputColVector.vector[i].checkPrecisionOverflow(outputColVector.precision); + } catch (ArithmeticException e) { // catch on error + outputColVector.noNulls = false; + outputColVector.isNull[i] = true; + } + } } diff --git a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/NullUtil.java b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/NullUtil.java index efd1a7c..5cc8218 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/NullUtil.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/NullUtil.java @@ -19,8 +19,6 @@ package org.apache.hadoop.hive.ql.exec.vector.expressions; import java.util.Arrays; - -import org.apache.hadoop.hive.common.type.Decimal128; import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector; import org.apache.hadoop.hive.ql.exec.vector.DoubleColumnVector; diff --git a/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorArithmeticExpressions.java b/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorArithmeticExpressions.java index 0b48ec8..312586e 100644 --- a/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorArithmeticExpressions.java +++ b/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorArithmeticExpressions.java @@ -29,6 +29,17 @@ import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector; import org.apache.hadoop.hive.ql.exec.vector.TestVectorizedRowBatch; import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColDivideDecimalScalar; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalScalarDivideDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalScalarModuloDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColAddDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColDivideDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColModuloDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColModuloDecimalScalar; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColMultiplyDecimalScalar; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColSubtractDecimalScalar; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColMultiplyDecimalColumn; +import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColSubtractDecimalColumn; import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.LongColAddLongColumn; import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.LongColAddLongScalar; import org.apache.hadoop.hive.ql.exec.vector.expressions.gen.DecimalColSubtractDecimalColumn; @@ -331,44 +342,6 @@ public void testDecimalColAddDecimalColumn() { // verify proper null output data value assertTrue(r.vector[0].equals(new Decimal128("0.01", (short) 2))); - } - - // Test decimal column-column addition - @Test - public void testDecimalColumnAddDecimalColumn() { - VectorizedRowBatch b = getVectorizedRowBatch3DecimalCols(); - VectorExpression expr = new DecimalColAddDecimalColumn(0, 1, 2); - DecimalColumnVector r = (DecimalColumnVector) b.cols[2]; - - // test without nulls - expr.evaluate(b); - assertTrue(r.vector[0].equals(new Decimal128("2.20", (short) 2))); - assertTrue(r.vector[1].equals(new Decimal128("-2.30", (short) 2))); - assertTrue(r.vector[2].equals(new Decimal128("1.00", (short) 2))); - - // test nulls propagation - b = getVectorizedRowBatch3DecimalCols(); - DecimalColumnVector c0 = (DecimalColumnVector) b.cols[0]; - c0.noNulls = false; - c0.isNull[0] = true; - r = (DecimalColumnVector) b.cols[2]; - expr.evaluate(b); - assertTrue(!r.noNulls && r.isNull[0]); - - // Verify null output data entry is not 0, but rather the value specified by design, - // which is the minimum non-0 value, 0.01 in this case. - assertTrue(r.vector[0].equals(new Decimal128("0.01", (short) 2))); - - // test that overflow produces NULL - b = getVectorizedRowBatch3DecimalCols(); - c0 = (DecimalColumnVector) b.cols[0]; - c0.vector[0].update("9999999999999999.99", (short) 2); // set to max possible value - r = (DecimalColumnVector) b.cols[2]; - expr.evaluate(b); // will cause overflow for result at position 0, must yield NULL - assertTrue(!r.noNulls && r.isNull[0]); - - // verify proper null output data value - assertTrue(r.vector[0].equals(new Decimal128("0.01", (short) 2))); // test left input repeating b = getVectorizedRowBatch3DecimalCols(); @@ -399,7 +372,7 @@ public void testDecimalColumnAddDecimalColumn() { // Spot check decimal column-column subtract @Test - public void testDecimalColumnSubtractDecimalColumn() { + public void testDecimalColSubtractDecimalColumn() { VectorizedRowBatch b = getVectorizedRowBatch3DecimalCols(); VectorExpression expr = new DecimalColSubtractDecimalColumn(0, 1, 2); DecimalColumnVector r = (DecimalColumnVector) b.cols[2]; @@ -421,7 +394,7 @@ public void testDecimalColumnSubtractDecimalColumn() { // Spot check decimal column-column multiply @Test - public void testDecimalColumnMultiplyDecimalColumn() { + public void testDecimalColMultiplyDecimalColumn() { VectorizedRowBatch b = getVectorizedRowBatch3DecimalCols(); VectorExpression expr = new DecimalColMultiplyDecimalColumn(0, 1, 2); DecimalColumnVector r = (DecimalColumnVector) b.cols[2]; @@ -500,6 +473,261 @@ public void testDecimalColAddDecimalScalar() { assertTrue(r.isNull[0]); } + /* Test decimal column to decimal scalar division. This is used to cover all the + * cases used in the source code template ColumnDivideScalarDecimal.txt. + * The template is used for division and modulo. + */ + @Test + public void testDecimalColDivideDecimalScalar() { + VectorizedRowBatch b = getVectorizedRowBatch3DecimalCols(); + Decimal128 d = new Decimal128("2.00", (short) 2); + VectorExpression expr = new DecimalColDivideDecimalScalar(0, d, 2); + + + // test without nulls + expr.evaluate(b); + DecimalColumnVector r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.vector[0].equals(new Decimal128("0.60", (short) 2))); + assertTrue(r.vector[1].equals(new Decimal128("-1.65", (short) 2))); + assertTrue(r.vector[2].equals(new Decimal128("0", (short) 2))); + + // test null propagation + b = getVectorizedRowBatch3DecimalCols(); + DecimalColumnVector in = (DecimalColumnVector) b.cols[0]; + r = (DecimalColumnVector) b.cols[2]; + in.noNulls = false; + in.isNull[0] = true; + expr.evaluate(b); + assertTrue(!r.noNulls); + assertTrue(r.isNull[0]); + + // test repeating case, no nulls + b = getVectorizedRowBatch3DecimalCols(); + in = (DecimalColumnVector) b.cols[0]; + in.isRepeating = true; + expr.evaluate(b); + r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.isRepeating); + assertTrue(r.vector[0].equals(new Decimal128("0.60", (short) 2))); + + // test repeating case for null value + b = getVectorizedRowBatch3DecimalCols(); + in = (DecimalColumnVector) b.cols[0]; + in.isRepeating = true; + in.isNull[0] = true; + in.noNulls = false; + expr.evaluate(b); + r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.isRepeating); + assertTrue(!r.noNulls); + assertTrue(r.isNull[0]); + + // test that zero-divide produces null for all output values + b = getVectorizedRowBatch3DecimalCols(); + in = (DecimalColumnVector) b.cols[0]; + expr = new DecimalColDivideDecimalScalar(0, new Decimal128("0", (short) 2), 2); + expr.evaluate(b); + r = (DecimalColumnVector) b.cols[2]; + assertFalse(r.noNulls); + assertTrue(r.isNull[0]); + assertTrue(r.isRepeating); + } + + /* Test decimal scalar divided column. This tests the primary logic + * for template ScalarDivideColumnDecimal.txt. + */ + @Test + public void testDecimalScalarDivideDecimalColumn() { + VectorizedRowBatch b = getVectorizedRowBatch3DecimalCols(); + Decimal128 d = new Decimal128("3.96", (short) 2); // 1.20 * 3.30 + VectorExpression expr = new DecimalScalarDivideDecimalColumn(d, 0, 2); + + // test without nulls + expr.evaluate(b); + DecimalColumnVector r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.vector[0].equals(new Decimal128("3.30", (short) 2))); + assertTrue(r.vector[1].equals(new Decimal128("-1.20", (short) 2))); + assertFalse(r.noNulls); // entry 2 is null due to zero-divide + assertTrue(r.isNull[2]); + + // test null propagation + b = getVectorizedRowBatch3DecimalCols(); + DecimalColumnVector in = (DecimalColumnVector) b.cols[0]; + r = (DecimalColumnVector) b.cols[2]; + in.noNulls = false; + in.isNull[0] = true; + expr.evaluate(b); + assertTrue(!r.noNulls); + assertTrue(r.isNull[0]); + + // test repeating case, no nulls + b = getVectorizedRowBatch3DecimalCols(); + in = (DecimalColumnVector) b.cols[0]; + in.isRepeating = true; + expr.evaluate(b); + r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.isRepeating); + assertTrue(r.vector[0].equals(new Decimal128("3.30", (short) 2))); + + // test repeating case for null value + b = getVectorizedRowBatch3DecimalCols(); + in = (DecimalColumnVector) b.cols[0]; + in.isRepeating = true; + in.isNull[0] = true; + in.noNulls = false; + expr.evaluate(b); + r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.isRepeating); + assertTrue(!r.noNulls); + assertTrue(r.isNull[0]); + } + + // Spot check Decimal Col-Scalar Modulo + @Test + public void testDecimalColModuloDecimalScalar() { + VectorizedRowBatch b = getVectorizedRowBatch3DecimalCols(); + Decimal128 d = new Decimal128("2.00", (short) 2); + VectorExpression expr = new DecimalColModuloDecimalScalar(0, d, 2); + + // test without nulls + expr.evaluate(b); + DecimalColumnVector r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.vector[0].equals(new Decimal128("1.20", (short) 2))); + assertTrue(r.vector[1].equals(new Decimal128("-1.30", (short) 2))); + assertTrue(r.vector[2].equals(new Decimal128("0", (short) 2))); + + // try again with some different data values and divisor + DecimalColumnVector in = (DecimalColumnVector) b.cols[0]; + in.vector[0].update("15.40", (short) 2); + in.vector[1].update("-17.20", (short) 2); + in.vector[2].update("70.00", (short) 2); + d.update("4.75", (short) 2); + + expr.evaluate(b); + assertTrue(r.vector[0].equals(new Decimal128("1.15", (short) 2))); + assertTrue(r.vector[1].equals(new Decimal128("-2.95", (short) 2))); + assertTrue(r.vector[2].equals(new Decimal128("3.50", (short) 2))); + + // try a zero-divide to show a repeating NULL is produced + d.update("0", (short) 2); + expr.evaluate(b); + assertFalse(r.noNulls); + assertTrue(r.isNull[0]); + assertTrue(r.isRepeating); + } + + // Spot check decimal scalar-column modulo + @Test + public void testDecimalScalarModuloDecimalColumn() { + VectorizedRowBatch b = getVectorizedRowBatch3DecimalCols(); + Decimal128 d = new Decimal128("2.00", (short) 2); + VectorExpression expr = new DecimalScalarModuloDecimalColumn(d, 0, 2); + + // test without nulls + expr.evaluate(b); + DecimalColumnVector r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.vector[0].equals(new Decimal128("0.80", (short) 2))); + assertTrue(r.vector[1].equals(new Decimal128("2.00", (short) 2))); + assertFalse(r.noNulls); // entry 2 will be null due to zero-divide + assertTrue(r.isNull[2]); + + // try again with some different data values + DecimalColumnVector in = (DecimalColumnVector) b.cols[0]; + in.vector[0].update("0.50", (short) 2); + in.vector[1].update("0.80", (short) 2); + in.vector[2].update("0.70", (short) 2); + + expr.evaluate(b); + assertTrue(r.vector[0].equals(new Decimal128("0.00", (short) 2))); + assertTrue(r.vector[1].equals(new Decimal128("0.40", (short) 2))); + assertTrue(r.vector[2].equals(new Decimal128("0.60", (short) 2))); + } + + @Test + public void testDecimalColDivideDecimalColumn() { + VectorizedRowBatch b = getVectorizedRowBatch3DecimalCols(); + DecimalColumnVector in1 = (DecimalColumnVector) b.cols[1]; + for (int i = 0; i < 3; i++) { + in1.vector[i] = new Decimal128("0.50", (short) 2); + } + VectorExpression expr = new DecimalColDivideDecimalColumn(0, 1, 2); + expr.evaluate(b); + DecimalColumnVector r = (DecimalColumnVector) b.cols[2]; + + // all divides are by 0.50 so the result column is 2 times col 0. + assertTrue(r.vector[0].equals(new Decimal128("2.40", (short) 2))); + assertTrue(r.vector[1].equals(new Decimal128("-6.60", (short) 2))); + assertTrue(r.vector[2].equals(new Decimal128("0", (short) 2))); + + // test null on left + b.cols[0].noNulls = false; + b.cols[0].isNull[0] = true; + expr.evaluate(b); + assertFalse(r.noNulls); + assertTrue(r.isNull[0]); + + // test null on right + b = getVectorizedRowBatch3DecimalCols(); + b.cols[1].noNulls = false; + b.cols[1].isNull[0] = true; + r = (DecimalColumnVector) b.cols[2]; + expr.evaluate(b); + assertFalse(r.noNulls); + assertTrue(r.isNull[0]); + + // test null on both sides + b = getVectorizedRowBatch3DecimalCols(); + b.cols[0].noNulls = false; + b.cols[0].isNull[0] = true; + b.cols[1].noNulls = false; + b.cols[1].isNull[0] = true; + expr.evaluate(b); + assertFalse(r.noNulls); + assertTrue(r.isNull[0]); + assertFalse(r.isNull[1]); + assertFalse(r.isNull[2]); + + // test repeating on left + b = getVectorizedRowBatch3DecimalCols(); + b.cols[0].isRepeating = true; + expr.evaluate(b); + r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.vector[2].equals(new Decimal128("1.20", (short) 2))); + + // test repeating on right + b = getVectorizedRowBatch3DecimalCols(); + b.cols[1].isRepeating = true; + expr.evaluate(b); + r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.vector[2].equals(new Decimal128("0", (short) 2))); + + // test both repeating + b = getVectorizedRowBatch3DecimalCols(); + b.cols[0].isRepeating = true; + b.cols[1].isRepeating = true; + expr.evaluate(b); + r = (DecimalColumnVector) b.cols[2]; + assertTrue(r.isRepeating); + assertTrue(r.vector[0].equals(new Decimal128("1.20", (short) 2))); + } + + // Spot check decimal column modulo decimal column + @Test + public void testDecimalColModuloDecimalColumn() { + VectorizedRowBatch b = getVectorizedRowBatch3DecimalCols(); + DecimalColumnVector in1 = (DecimalColumnVector) b.cols[1]; + for (int i = 0; i < 3; i++) { + in1.vector[i] = new Decimal128("0.50", (short) 2); + } + VectorExpression expr = new DecimalColModuloDecimalColumn(0, 1, 2); + expr.evaluate(b); + DecimalColumnVector r = (DecimalColumnVector) b.cols[2]; + + assertTrue(r.vector[0].equals(new Decimal128("0.20", (short) 2))); + assertTrue(r.vector[1].equals(new Decimal128("-0.30", (short) 2))); + assertTrue(r.vector[2].equals(new Decimal128("0", (short) 2))); + } + /* Spot check correctness of decimal column subtract decimal scalar. The case for * addition checks all the cases for the template, so don't do that redundantly here. */