This is an automated email from the ASF dual-hosted git repository.
kasakrisz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git
The following commit(s) were added to refs/heads/master by this push:
new c018fab39b4 HIVE-28329: Vectorized multiplication of CASE expression
and decimal column incorrectly returns 0 (#6575)
c018fab39b4 is described below
commit c018fab39b4015f953edd9e5c68769add5238fc2
Author: kokila-19 <[email protected]>
AuthorDate: Fri Jul 24 13:43:38 2026 +0530
HIVE-28329: Vectorized multiplication of CASE expression and decimal column
incorrectly returns 0 (#6575)
Preserve inputColumnNum column refs when re-instantiating DECIMAL parents
after DECIMAL_64 child conversion and refresh projectedOutputColumns.
---
.../hive/ql/optimizer/physical/Vectorizer.java | 104 +++++++--
...tor_decimal64_col_multiply_case_subquery_join.q | 21 ++
...decimal64_col_multiply_case_subquery_join.q.out | 250 +++++++++++++++++++++
3 files changed, 358 insertions(+), 17 deletions(-)
diff --git
a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/Vectorizer.java
b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/Vectorizer.java
index 82f81861a4d..143d7de77b6 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/Vectorizer.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/optimizer/physical/Vectorizer.java
@@ -102,6 +102,7 @@
import
org.apache.hadoop.hive.ql.exec.vector.VectorizationContext.InConstantType;
import org.apache.hadoop.hive.ql.exec.vector.VectorizationContextRegion;
import org.apache.hadoop.hive.ql.exec.vector.VectorizedSupport.Support;
+import
org.apache.hadoop.hive.ql.exec.vector.expressions.ConvertDecimal64ToDecimal;
import org.apache.hadoop.hive.ql.exec.vector.expressions.IdentityExpression;
import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpression;
import
org.apache.hadoop.hive.ql.exec.vector.expressions.aggregates.VectorAggregateExpression;
@@ -4796,15 +4797,19 @@ public static Operator<? extends OperatorDesc>
vectorizeSelectOperator(
vContext.markActualScratchColumns();
VectorExpression[] vectorSelectExprs = new VectorExpression[size];
+ VectorExpression[] selectColumnExprs = new VectorExpression[size];
int[] projectedOutputColumns = new int[size];
+ int[] vectorSelectExprIndexForCol = new int[size];
+ Arrays.fill(vectorSelectExprIndexForCol, -1);
for (int i = 0; i < size; i++) {
ExprNodeDesc expr = colList.get(i);
VectorExpression ve = vContext.getVectorExpression(expr);
- projectedOutputColumns[i] = ve.getOutputColumnNum();
+ selectColumnExprs[i] = ve;
if (ve instanceof IdentityExpression) {
// Suppress useless evaluation.
continue;
}
+ vectorSelectExprIndexForCol[i] = index;
vectorSelectExprs[index++] = ve;
}
if (index < size) {
@@ -4817,6 +4822,13 @@ public static Operator<? extends OperatorDesc>
vectorizeSelectOperator(
// The following method introduces a cast if x or y is DECIMAL_64 and
parent expression (x % y) is DECIMAL.
try {
fixDecimalDataTypePhysicalVariations(vContext, vectorSelectExprs);
+ for (int i = 0; i < size; i++) {
+ int exprIndex = vectorSelectExprIndexForCol[i];
+ VectorExpression ve = (exprIndex >= 0)
+ ? vectorSelectExprs[exprIndex]
+ : selectColumnExprs[i];
+ projectedOutputColumns[i] = ve.getOutputColumnNum();
+ }
} finally {
vContext.freeMarkedScratchColumns();
}
@@ -4879,7 +4891,6 @@ private static VectorExpression
fixDecimalDataTypePhysicalVariations(final Vecto
}
} else {
Object[] arguments;
- int argumentCount = children.length + (parent.getOutputColumnNum()
== -1 ? 0 : 1);
// VectorCoalesce receives arguments as an array.
// Need to handle it as a special case to avoid instantiation
failure.
if (parent instanceof VectorCoalesce) {
@@ -4891,20 +4902,7 @@ private static VectorExpression
fixDecimalDataTypePhysicalVariations(final Vecto
}
arguments[1] = parent.getOutputColumnNum();
} else {
- if (parent instanceof DecimalColDivideDecimalScalar) {
- arguments = new Object[argumentCount + 1];
- arguments[children.length] = ((DecimalColDivideDecimalScalar)
parent).getValue();
- } else {
- arguments = new Object[argumentCount];
- }
- for (int i = 0; i < children.length; i++) {
- VectorExpression vce = children[i];
- arguments[i] = vce.getOutputColumnNum();
- }
- }
- // retain output column number from parent
- if (parent.getOutputColumnNum() != -1) {
- arguments[arguments.length - 1] = parent.getOutputColumnNum();
+ arguments = buildReinstantiationArgsForDecimal64(parent, children);
}
// re-instantiate the parent expression with new arguments
VectorExpression newParent =
vContext.instantiateExpression(parent.getClass(), parent.getOutputTypeInfo(),
@@ -4913,7 +4911,7 @@ private static VectorExpression
fixDecimalDataTypePhysicalVariations(final Vecto
newParent.setOutputDataTypePhysicalVariation(parent.getOutputDataTypePhysicalVariation());
newParent.setInputTypeInfos(parent.getInputTypeInfos());
newParent.setInputDataTypePhysicalVariations(dataTypePhysicalVariations);
- newParent.setChildExpressions(parent.getChildExpressions());
+ newParent.setChildExpressions(children);
return newParent;
}
}
@@ -4921,6 +4919,78 @@ private static VectorExpression
fixDecimalDataTypePhysicalVariations(final Vecto
return parent;
}
+ /**
+ * Rebuild constructor arguments for a vector expression after wrapping
DECIMAL_64 child
+ * expressions with {@link ConvertDecimal64ToDecimal}. Column reference
inputs live in
+ * {@link VectorExpression#inputColumnNum} and are not included in
childExpressions, so they
+ * must be preserved when re-instantiating the parent.
+ */
+ static Object[] buildReinstantiationArgsForDecimal64(VectorExpression parent,
+ VectorExpression[] children) {
+ int[] inputColNums = extractParentInputColumnNums(parent);
+ replaceInputColsWithConvertedChildOutputs(inputColNums, children);
+ return buildParentConstructorArguments(inputColNums, parent);
+ }
+
+ private static int[] extractParentInputColumnNums(VectorExpression parent) {
+ int inputCount = 0;
+ for (int col : parent.inputColumnNum) {
+ if (col != -1) {
+ inputCount++;
+ }
+ }
+
+ int[] inputColNums = new int[inputCount];
+ int idx = 0;
+ for (int col : parent.inputColumnNum) {
+ if (col != -1) {
+ inputColNums[idx++] = col;
+ }
+ }
+ return inputColNums;
+ }
+
+ /**
+ * For each wrapped DECIMAL_64 child, replace its pre-conversion input
column slot in
+ * {@code inputColNums} with the {@link ConvertDecimal64ToDecimal} output
column.
+ */
+ private static void replaceInputColsWithConvertedChildOutputs(int[]
inputColNums,
+ VectorExpression[] children) {
+ for (VectorExpression child : children) {
+ int preConversionCol = getPreConversionColumnNum(child);
+ int convertedCol = child.getOutputColumnNum();
+ for (int i = 0; i < inputColNums.length; i++) {
+ if (inputColNums[i] == preConversionCol) {
+ inputColNums[i] = convertedCol;
+ break;
+ }
+ }
+ }
+ }
+
+ private static Object[] buildParentConstructorArguments(int[] inputColNums,
+ VectorExpression parent) {
+ int extraArgs = parent instanceof DecimalColDivideDecimalScalar ? 2 : 1;
+ Object[] arguments = new Object[inputColNums.length + extraArgs];
+ for (int i = 0; i < inputColNums.length; i++) {
+ arguments[i] = inputColNums[i];
+ }
+ int outputIndex = inputColNums.length;
+ if (parent instanceof DecimalColDivideDecimalScalar) {
+ arguments[outputIndex++] = ((DecimalColDivideDecimalScalar)
parent).getValue();
+ }
+ arguments[outputIndex] = parent.getOutputColumnNum();
+ return arguments;
+ }
+
+ /** Column to match in {@code inputColNums} before replacing with a
converted child output. */
+ private static int getPreConversionColumnNum(VectorExpression child) {
+ if (child instanceof ConvertDecimal64ToDecimal) {
+ return child.inputColumnNum[0];
+ }
+ return child.getOutputColumnNum();
+ }
+
private static void fillInPTFEvaluators(
List<WindowFunctionDef> windowsFunctions,
String[] evaluatorFunctionNames,
diff --git
a/ql/src/test/queries/clientpositive/vector_decimal64_col_multiply_case_subquery_join.q
b/ql/src/test/queries/clientpositive/vector_decimal64_col_multiply_case_subquery_join.q
new file mode 100644
index 00000000000..ed4cb9365ba
--- /dev/null
+++
b/ql/src/test/queries/clientpositive/vector_decimal64_col_multiply_case_subquery_join.q
@@ -0,0 +1,21 @@
+CREATE TABLE test1(col2 varchar(28), col3 decimal(26,6), col4 varchar(28));
+CREATE TABLE test2(col4 varchar(28));
+
+INSERT INTO test1 VALUES ('abc', 1.00, 'DEF');
+INSERT INTO test2 VALUES ('DEF');
+
+EXPLAIN VECTORIZATION DETAIL
+SELECT
+ aa.col3 * CASE WHEN aa.col2 = 'bc' THEN 1.77 ELSE 0.72 END AS int_cost
+FROM test1 aa
+ INNER JOIN
+ test2 bb ON aa.col4 = bb.col4;
+
+SELECT
+ aa.col3 * CASE WHEN aa.col2 = 'bc' THEN 1.77 ELSE 0.72 END AS int_cost
+FROM test1 aa
+ INNER JOIN
+ test2 bb ON aa.col4 = bb.col4;
+
+DROP TABLE test1;
+DROP TABLE test2;
\ No newline at end of file
diff --git
a/ql/src/test/results/clientpositive/llap/vector_decimal64_col_multiply_case_subquery_join.q.out
b/ql/src/test/results/clientpositive/llap/vector_decimal64_col_multiply_case_subquery_join.q.out
new file mode 100644
index 00000000000..e260248c4a1
--- /dev/null
+++
b/ql/src/test/results/clientpositive/llap/vector_decimal64_col_multiply_case_subquery_join.q.out
@@ -0,0 +1,250 @@
+PREHOOK: query: CREATE TABLE test1(col2 varchar(28), col3 decimal(26,6), col4
varchar(28))
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@test1
+POSTHOOK: query: CREATE TABLE test1(col2 varchar(28), col3 decimal(26,6), col4
varchar(28))
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@test1
+PREHOOK: query: CREATE TABLE test2(col4 varchar(28))
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@test2
+POSTHOOK: query: CREATE TABLE test2(col4 varchar(28))
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@test2
+PREHOOK: query: INSERT INTO test1 VALUES ('abc', 1.00, 'DEF')
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@test1
+POSTHOOK: query: INSERT INTO test1 VALUES ('abc', 1.00, 'DEF')
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@test1
+POSTHOOK: Lineage: test1.col2 SCRIPT []
+POSTHOOK: Lineage: test1.col3 SCRIPT []
+POSTHOOK: Lineage: test1.col4 SCRIPT []
+PREHOOK: query: INSERT INTO test2 VALUES ('DEF')
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@test2
+POSTHOOK: query: INSERT INTO test2 VALUES ('DEF')
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@test2
+POSTHOOK: Lineage: test2.col4 SCRIPT []
+PREHOOK: query: EXPLAIN VECTORIZATION DETAIL
+SELECT
+ aa.col3 * CASE WHEN aa.col2 = 'bc' THEN 1.77 ELSE 0.72 END AS int_cost
+FROM test1 aa
+ INNER JOIN
+ test2 bb ON aa.col4 = bb.col4
+PREHOOK: type: QUERY
+PREHOOK: Input: default@test1
+PREHOOK: Input: default@test2
+#### A masked pattern was here ####
+POSTHOOK: query: EXPLAIN VECTORIZATION DETAIL
+SELECT
+ aa.col3 * CASE WHEN aa.col2 = 'bc' THEN 1.77 ELSE 0.72 END AS int_cost
+FROM test1 aa
+ INNER JOIN
+ test2 bb ON aa.col4 = bb.col4
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@test1
+POSTHOOK: Input: default@test2
+#### A masked pattern was here ####
+PLAN VECTORIZATION:
+ enabled: true
+ enabledConditionsMet: [hive.vectorized.execution.enabled IS true]
+
+STAGE DEPENDENCIES:
+ Stage-1 is a root stage
+ Stage-0 depends on stages: Stage-1
+
+STAGE PLANS:
+ Stage: Stage-1
+ Tez
+#### A masked pattern was here ####
+ Edges:
+ Reducer 2 <- Map 1 (SIMPLE_EDGE), Map 3 (SIMPLE_EDGE)
+#### A masked pattern was here ####
+ Vertices:
+ Map 1
+ Map Operator Tree:
+ TableScan
+ alias: aa
+ filterExpr: col4 is not null (type: boolean)
+ Statistics: Num rows: 1 Data size: 286 Basic stats: COMPLETE
Column stats: COMPLETE
+ TableScan Vectorization:
+ native: true
+ vectorizationSchemaColumns: [0:col2:varchar(28),
1:col3:decimal(26,6), 2:col4:varchar(28),
3:ROW__ID:struct<writeid:bigint,bucketid:int,rowid:bigint>,
4:ROW__IS__DELETED:boolean]
+ Filter Operator
+ Filter Vectorization:
+ className: VectorFilterOperator
+ native: true
+ predicateExpression: SelectColumnIsNotNull(col
2:varchar(28))
+ predicate: col4 is not null (type: boolean)
+ Statistics: Num rows: 1 Data size: 286 Basic stats:
COMPLETE Column stats: COMPLETE
+ Select Operator
+ expressions: col4 (type: varchar(28)), (col3 * if((col2
= 'bc'), 1.77, 0.72)) (type: decimal(30,8))
+ outputColumnNames: _col0, _col1
+ Select Vectorization:
+ className: VectorSelectOperator
+ native: true
+ projectedOutputColumnNums: [2, 7]
+ selectExpressions:
DecimalColMultiplyDecimalColumn(col 1:decimal(26,6), col
8:decimal(3,2)/DECIMAL_64)(children: ConvertDecimal64ToDecimal(col
6:decimal(3,2)/DECIMAL_64)(children: IfExprDecimal64ScalarDecimal64Scalar(col
5:boolean, decimal64Val1 177, decimalVal1 1.77, decimal64Val2 72, decimalVal2
0.72)(children: StringGroupColEqualVarCharScalar(col 0:varchar(28), val bc) ->
5:boolean) -> 6:decimal(3,2)/DECIMAL_64) -> 8:decimal(3,2)) -> 7:decimal(30,8)
+ Statistics: Num rows: 1 Data size: 199 Basic stats:
COMPLETE Column stats: COMPLETE
+ Reduce Output Operator
+ key expressions: _col0 (type: varchar(28))
+ null sort order: z
+ sort order: +
+ Map-reduce partition columns: _col0 (type: varchar(28))
+ Reduce Sink Vectorization:
+ className: VectorReduceSinkStringOperator
+ keyColumns: 2:varchar(28)
+ native: true
+ nativeConditionsMet:
hive.vectorized.execution.reducesink.new.enabled IS true, hive.execution.engine
tez IN [tez] IS true, No PTF TopN IS true, No DISTINCT columns IS true,
BinarySortableSerDe for keys IS true, LazyBinarySerDe for values IS true
+ valueColumns: 7:decimal(30,8)
+ Statistics: Num rows: 1 Data size: 199 Basic stats:
COMPLETE Column stats: COMPLETE
+ value expressions: _col1 (type: decimal(30,8))
+ Execution mode: vectorized, llap
+ LLAP IO: all inputs
+ Map Vectorization:
+ enabled: true
+ enabledConditionsMet:
hive.vectorized.use.vector.serde.deserialize IS true
+ inputFormatFeatureSupport: [DECIMAL_64]
+ featureSupportInUse: [DECIMAL_64]
+ inputFileFormats: org.apache.hadoop.mapred.TextInputFormat
+ allNative: true
+ usesVectorUDFAdaptor: false
+ vectorized: true
+ rowBatchContext:
+ dataColumnCount: 3
+ includeColumns: [0, 1, 2]
+ dataColumns: col2:varchar(28), col3:decimal(26,6),
col4:varchar(28)
+ partitionColumnCount: 0
+ scratchColumnTypeNames: [bigint, decimal(3,2)/DECIMAL_64,
decimal(30,8), decimal(3,2)]
+ Map 3
+ Map Operator Tree:
+ TableScan
+ alias: bb
+ filterExpr: col4 is not null (type: boolean)
+ Statistics: Num rows: 1 Data size: 87 Basic stats: COMPLETE
Column stats: COMPLETE
+ TableScan Vectorization:
+ native: true
+ vectorizationSchemaColumns: [0:col4:varchar(28),
1:ROW__ID:struct<writeid:bigint,bucketid:int,rowid:bigint>,
2:ROW__IS__DELETED:boolean]
+ Filter Operator
+ Filter Vectorization:
+ className: VectorFilterOperator
+ native: true
+ predicateExpression: SelectColumnIsNotNull(col
0:varchar(28))
+ predicate: col4 is not null (type: boolean)
+ Statistics: Num rows: 1 Data size: 87 Basic stats:
COMPLETE Column stats: COMPLETE
+ Select Operator
+ expressions: col4 (type: varchar(28))
+ outputColumnNames: _col0
+ Select Vectorization:
+ className: VectorSelectOperator
+ native: true
+ projectedOutputColumnNums: [0]
+ Statistics: Num rows: 1 Data size: 87 Basic stats:
COMPLETE Column stats: COMPLETE
+ Reduce Output Operator
+ key expressions: _col0 (type: varchar(28))
+ null sort order: z
+ sort order: +
+ Map-reduce partition columns: _col0 (type: varchar(28))
+ Reduce Sink Vectorization:
+ className: VectorReduceSinkStringOperator
+ keyColumns: 0:varchar(28)
+ native: true
+ nativeConditionsMet:
hive.vectorized.execution.reducesink.new.enabled IS true, hive.execution.engine
tez IN [tez] IS true, No PTF TopN IS true, No DISTINCT columns IS true,
BinarySortableSerDe for keys IS true, LazyBinarySerDe for values IS true
+ Statistics: Num rows: 1 Data size: 87 Basic stats:
COMPLETE Column stats: COMPLETE
+ Execution mode: vectorized, llap
+ LLAP IO: all inputs
+ Map Vectorization:
+ enabled: true
+ enabledConditionsMet:
hive.vectorized.use.vector.serde.deserialize IS true
+ inputFormatFeatureSupport: [DECIMAL_64]
+ featureSupportInUse: [DECIMAL_64]
+ inputFileFormats: org.apache.hadoop.mapred.TextInputFormat
+ allNative: true
+ usesVectorUDFAdaptor: false
+ vectorized: true
+ rowBatchContext:
+ dataColumnCount: 1
+ includeColumns: [0]
+ dataColumns: col4:varchar(28)
+ partitionColumnCount: 0
+ scratchColumnTypeNames: []
+ Reducer 2
+ Execution mode: llap
+ Reduce Operator Tree:
+ Merge Join Operator
+ condition map:
+ Inner Join 0 to 1
+ keys:
+ 0 _col0 (type: varchar(28))
+ 1 _col0 (type: varchar(28))
+ outputColumnNames: _col1
+ Statistics: Num rows: 1 Data size: 112 Basic stats: COMPLETE
Column stats: COMPLETE
+ Select Operator
+ expressions: _col1 (type: decimal(30,8))
+ outputColumnNames: _col0
+ Statistics: Num rows: 1 Data size: 112 Basic stats: COMPLETE
Column stats: COMPLETE
+ File Output Operator
+ compressed: false
+ Statistics: Num rows: 1 Data size: 112 Basic stats:
COMPLETE Column stats: COMPLETE
+ table:
+ input format:
org.apache.hadoop.mapred.SequenceFileInputFormat
+ output format:
org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
+ serde:
org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
+ MergeJoin Vectorization:
+ enabled: false
+ enableConditionsNotMet: Vectorizing MergeJoin Supported IS
false
+
+ Stage: Stage-0
+ Fetch Operator
+ limit: -1
+ Processor Tree:
+ ListSink
+
+PREHOOK: query: SELECT
+ aa.col3 * CASE WHEN aa.col2 = 'bc' THEN 1.77 ELSE 0.72 END AS int_cost
+FROM test1 aa
+ INNER JOIN
+ test2 bb ON aa.col4 = bb.col4
+PREHOOK: type: QUERY
+PREHOOK: Input: default@test1
+PREHOOK: Input: default@test2
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT
+ aa.col3 * CASE WHEN aa.col2 = 'bc' THEN 1.77 ELSE 0.72 END AS int_cost
+FROM test1 aa
+ INNER JOIN
+ test2 bb ON aa.col4 = bb.col4
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@test1
+POSTHOOK: Input: default@test2
+#### A masked pattern was here ####
+0.72000000
+PREHOOK: query: DROP TABLE test1
+PREHOOK: type: DROPTABLE
+PREHOOK: Input: default@test1
+PREHOOK: Output: database:default
+PREHOOK: Output: default@test1
+POSTHOOK: query: DROP TABLE test1
+POSTHOOK: type: DROPTABLE
+POSTHOOK: Input: default@test1
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@test1
+PREHOOK: query: DROP TABLE test2
+PREHOOK: type: DROPTABLE
+PREHOOK: Input: default@test2
+PREHOOK: Output: database:default
+PREHOOK: Output: default@test2
+POSTHOOK: query: DROP TABLE test2
+POSTHOOK: type: DROPTABLE
+POSTHOOK: Input: default@test2
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@test2