Skip to content

Commit 946bbe4

Browse files
authored
HIVE-29447: Incorrect nullability inference when transforming SEARCH expression back to disjunctions (#6318)
1 parent 54d8741 commit 946bbe4

File tree

3 files changed

+107
-3
lines changed

3 files changed

+107
-3
lines changed

ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/SearchTransformer.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,24 @@ public class SearchTransformer<C extends Comparable<C>> {
5959
private final RexNode ref;
6060
private final Sarg<C> sarg;
6161
private final RexUnknownAs unknownContext;
62-
protected final RelDataType type;
62+
protected final RelDataType operandType;
63+
protected final RelDataType expressionType;
6364

6465
public SearchTransformer(RexBuilder rexBuilder, RexCall search, final RexUnknownAs unknownContext) {
6566
this.rexBuilder = rexBuilder;
6667
ref = search.getOperands().get(0);
6768
RexLiteral literal = (RexLiteral) search.operands.get(1);
6869
sarg = Objects.requireNonNull(literal.getValueAs(Sarg.class), "Sarg");
69-
type = literal.getType();
70+
operandType = literal.getType();
71+
expressionType = search.getType();
7072
this.unknownContext = unknownContext;
7173
}
7274

7375
public RexNode transform() {
7476
PerfLogger perfLogger = SessionState.getPerfLogger();
7577
perfLogger.perfLogBegin(this.getClass().getName(), PerfLogger.SEARCH_TRANSFORMER);
7678

77-
RangeConverter<C> consumer = new RangeConverter<>(rexBuilder, type, ref);
79+
RangeConverter<C> consumer = new RangeConverter<>(rexBuilder, operandType, ref);
7880
RangeSets.forEach(sarg.rangeSet, consumer);
7981

8082
List<RexNode> orList = new ArrayList<>();
@@ -101,6 +103,11 @@ public RexNode transform() {
101103
x = RexUtil.composeConjunction(rexBuilder, Arrays.asList(notNull, x));
102104
}
103105
perfLogger.perfLogEnd(this.getClass().getName(), PerfLogger.SEARCH_TRANSFORMER);
106+
107+
if (!expressionType.equals(x.getType()) && x instanceof RexCall transformedCall) {
108+
x = rexBuilder.makeCall(expressionType, transformedCall.getOperator(), transformedCall.getOperands());
109+
}
110+
104111
return x;
105112
}
106113

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
CREATE TABLE main_table (
2+
id_col STRING,
3+
join_key STRING,
4+
int_col_to_test INT,
5+
misc_attr STRING
6+
);
7+
8+
CREATE TABLE lookup_table (
9+
lookup_key STRING,
10+
replacement_val INT
11+
);
12+
13+
-- Test search to disjuncts conversion when search is in a Project
14+
EXPLAIN CBO
15+
SELECT
16+
CASE
17+
WHEN base.int_col_to_test IS NULL
18+
OR base.int_col_to_test = ''
19+
OR base.int_col_to_test = 0
20+
THEN COALESCE(lkup.replacement_val, 0)
21+
ELSE base.int_col_to_test
22+
END AS final_calculated_column
23+
FROM main_table base
24+
JOIN lookup_table lkup ON base.join_key = lkup.lookup_key
25+
;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
PREHOOK: query: CREATE TABLE main_table (
2+
id_col STRING,
3+
join_key STRING,
4+
int_col_to_test INT,
5+
misc_attr STRING
6+
)
7+
PREHOOK: type: CREATETABLE
8+
PREHOOK: Output: database:default
9+
PREHOOK: Output: default@main_table
10+
POSTHOOK: query: CREATE TABLE main_table (
11+
id_col STRING,
12+
join_key STRING,
13+
int_col_to_test INT,
14+
misc_attr STRING
15+
)
16+
POSTHOOK: type: CREATETABLE
17+
POSTHOOK: Output: database:default
18+
POSTHOOK: Output: default@main_table
19+
PREHOOK: query: CREATE TABLE lookup_table (
20+
lookup_key STRING,
21+
replacement_val INT
22+
)
23+
PREHOOK: type: CREATETABLE
24+
PREHOOK: Output: database:default
25+
PREHOOK: Output: default@lookup_table
26+
POSTHOOK: query: CREATE TABLE lookup_table (
27+
lookup_key STRING,
28+
replacement_val INT
29+
)
30+
POSTHOOK: type: CREATETABLE
31+
POSTHOOK: Output: database:default
32+
POSTHOOK: Output: default@lookup_table
33+
PREHOOK: query: EXPLAIN CBO
34+
SELECT
35+
CASE
36+
WHEN base.int_col_to_test IS NULL
37+
OR base.int_col_to_test = ''
38+
OR base.int_col_to_test = 0
39+
THEN COALESCE(lkup.replacement_val, 0)
40+
ELSE base.int_col_to_test
41+
END AS final_calculated_column
42+
FROM main_table base
43+
JOIN lookup_table lkup ON base.join_key = lkup.lookup_key
44+
PREHOOK: type: QUERY
45+
PREHOOK: Input: default@lookup_table
46+
PREHOOK: Input: default@main_table
47+
#### A masked pattern was here ####
48+
POSTHOOK: query: EXPLAIN CBO
49+
SELECT
50+
CASE
51+
WHEN base.int_col_to_test IS NULL
52+
OR base.int_col_to_test = ''
53+
OR base.int_col_to_test = 0
54+
THEN COALESCE(lkup.replacement_val, 0)
55+
ELSE base.int_col_to_test
56+
END AS final_calculated_column
57+
FROM main_table base
58+
JOIN lookup_table lkup ON base.join_key = lkup.lookup_key
59+
POSTHOOK: type: QUERY
60+
POSTHOOK: Input: default@lookup_table
61+
POSTHOOK: Input: default@main_table
62+
#### A masked pattern was here ####
63+
CBO PLAN:
64+
HiveProject(final_calculated_column=[CASE($2, $4, $1)])
65+
HiveJoin(condition=[=($0, $3)], joinType=[inner], algorithm=[none], cost=[not available])
66+
HiveProject(join_key=[$1], int_col_to_test=[$2], EXPR$0=[OR(IS NULL($2), =($2, 0))])
67+
HiveFilter(condition=[IS NOT NULL($1)])
68+
HiveTableScan(table=[[default, main_table]], table:alias=[base])
69+
HiveProject(lookup_key=[$0], EXPR$0=[CASE(IS NOT NULL($1), $1, 0)])
70+
HiveFilter(condition=[IS NOT NULL($0)])
71+
HiveTableScan(table=[[default, lookup_table]], table:alias=[lkup])
72+

0 commit comments

Comments
 (0)