[fix](planner) the project expr should be calculated in join node in some case (#17035)
Consider the sql bellow:
select sum(cc.qlnm) as qlnm
FROM
outerjoin_A
left join (SELECT
outerjoin_B.b,
coalesce(outerjoin_C.c, 0) AS qlnm
FROM
outerjoin_B
inner JOIN outerjoin_C ON outerjoin_B.b = outerjoin_C.c
) cc on outerjoin_A.a = cc.b
group by outerjoin_A.a;
The coalesce(outerjoin_C.c, 0) was calculated in the agg node, which is wrong.
This pr correct this, and the expr is calculated in the inner join node now.
This commit is contained in:
@ -137,6 +137,10 @@ public final class ExprSubstitutionMap {
|
||||
lhs = lhsExprList;
|
||||
}
|
||||
|
||||
public void updateRhsExprs(List<Expr> rhsExprList) {
|
||||
rhs = rhsExprList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a map which is equivalent to applying f followed by g,
|
||||
* i.e., g(f()).
|
||||
|
||||
@ -586,4 +586,36 @@ public abstract class JoinNodeBase extends PlanNode {
|
||||
public void setvSrcToOutputSMap(List<Expr> lhs) {
|
||||
this.vSrcToOutputSMap = new ExprSubstitutionMap(lhs, Collections.emptyList());
|
||||
}
|
||||
|
||||
public void setOutputSmap(ExprSubstitutionMap smap, Analyzer analyzer) {
|
||||
outputSmap = smap;
|
||||
ExprSubstitutionMap tmpSmap = new ExprSubstitutionMap(Lists.newArrayList(vSrcToOutputSMap.getRhs()),
|
||||
Lists.newArrayList(vSrcToOutputSMap.getLhs()));
|
||||
List<Expr> newRhs = Lists.newArrayList();
|
||||
boolean bSmapChanged = false;
|
||||
for (Expr expr : smap.getRhs()) {
|
||||
if (expr instanceof SlotRef) {
|
||||
newRhs.add(expr);
|
||||
} else {
|
||||
// we need do project in the join node
|
||||
// add a new slot for projection result and add the project expr to vSrcToOutputSMap
|
||||
SlotDescriptor slotDesc = analyzer.addSlotDescriptor(vOutputTupleDesc);
|
||||
slotDesc.initFromExpr(expr);
|
||||
slotDesc.setIsMaterialized(true);
|
||||
// the project expr is from smap, which use the slots of hash join node's output tuple
|
||||
// we need substitute it to make sure the project expr use slots from intermediate tuple
|
||||
expr.substitute(tmpSmap);
|
||||
vSrcToOutputSMap.getLhs().add(expr);
|
||||
SlotRef slotRef = new SlotRef(slotDesc);
|
||||
vSrcToOutputSMap.getRhs().add(slotRef);
|
||||
newRhs.add(slotRef);
|
||||
bSmapChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bSmapChanged) {
|
||||
outputSmap.updateRhsExprs(newRhs);
|
||||
vOutputTupleDesc.computeStatAndMemLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -692,7 +692,7 @@ public abstract class PlanNode extends TreeNode<PlanNode> implements PlanStats {
|
||||
return outputSmap;
|
||||
}
|
||||
|
||||
public void setOutputSmap(ExprSubstitutionMap smap) {
|
||||
public void setOutputSmap(ExprSubstitutionMap smap, Analyzer analyzer) {
|
||||
outputSmap = smap;
|
||||
}
|
||||
|
||||
|
||||
@ -204,7 +204,7 @@ public class SingleNodePlanner {
|
||||
// Set the output smap to resolve exprs referencing inline views within stmt.
|
||||
// Not needed for a UnionStmt because it materializes its input operands.
|
||||
if (stmt instanceof SelectStmt) {
|
||||
node.setOutputSmap(((SelectStmt) stmt).getBaseTblSmap());
|
||||
node.setOutputSmap(((SelectStmt) stmt).getBaseTblSmap(), analyzer);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@ -320,7 +320,7 @@ public class SingleNodePlanner {
|
||||
scanNodes.removeIf(scanNode -> scanTupleIds.contains(scanNode.getTupleIds().get(0)));
|
||||
PlanNode node = createEmptyNode(root, stmt, analyzer);
|
||||
// Ensure result exprs will be substituted by right outputSmap
|
||||
node.setOutputSmap(root.outputSmap);
|
||||
node.setOutputSmap(root.outputSmap, analyzer);
|
||||
return node;
|
||||
}
|
||||
|
||||
@ -1149,7 +1149,7 @@ public class SingleNodePlanner {
|
||||
}
|
||||
final PlanNode emptySetNode = new EmptySetNode(ctx.getNextNodeId(), rowTuples);
|
||||
emptySetNode.init(analyzer);
|
||||
emptySetNode.setOutputSmap(selectStmt.getBaseTblSmap());
|
||||
emptySetNode.setOutputSmap(selectStmt.getBaseTblSmap(), analyzer);
|
||||
return createAggregationPlan(selectStmt, analyzer, emptySetNode);
|
||||
}
|
||||
|
||||
@ -1588,7 +1588,7 @@ public class SingleNodePlanner {
|
||||
// conjuncts into outer-joined inline views, so hasEmptyResultSet() cannot be
|
||||
// true for an outer-joined inline view that has no table refs.
|
||||
Preconditions.checkState(!analyzer.isOuterJoined(inlineViewRef.getId()));
|
||||
emptySetNode.setOutputSmap(inlineViewRef.getSmap());
|
||||
emptySetNode.setOutputSmap(inlineViewRef.getSmap(), analyzer);
|
||||
return emptySetNode;
|
||||
}
|
||||
// Analysis should have generated a tuple id into which to materialize the exprs.
|
||||
@ -1605,7 +1605,7 @@ public class SingleNodePlanner {
|
||||
unionNode.init(analyzer);
|
||||
//set outputSmap to substitute literal in outputExpr
|
||||
unionNode.setWithoutTupleIsNullOutputSmap(inlineViewRef.getSmap());
|
||||
unionNode.setOutputSmap(inlineViewRef.getSmap());
|
||||
unionNode.setOutputSmap(inlineViewRef.getSmap(), analyzer);
|
||||
if (analyzer.isOuterJoined(inlineViewRef.getId())) {
|
||||
List<Expr> nullableRhs;
|
||||
if (analyzer.isOuterJoinedLeftSide(inlineViewRef.getId())) {
|
||||
@ -1615,7 +1615,8 @@ public class SingleNodePlanner {
|
||||
nullableRhs = TupleIsNullPredicate.wrapExprs(inlineViewRef.getSmap().getRhs(),
|
||||
unionNode.getTupleIds(), TNullSide.RIGHT, analyzer);
|
||||
}
|
||||
unionNode.setOutputSmap(new ExprSubstitutionMap(inlineViewRef.getSmap().getLhs(), nullableRhs));
|
||||
unionNode.setOutputSmap(
|
||||
new ExprSubstitutionMap(inlineViewRef.getSmap().getLhs(), nullableRhs), analyzer);
|
||||
}
|
||||
return unionNode;
|
||||
}
|
||||
@ -1646,7 +1647,7 @@ public class SingleNodePlanner {
|
||||
outputSmap = new ExprSubstitutionMap(outputSmap.getLhs(), nullableRhs);
|
||||
}
|
||||
// Set output smap of rootNode *before* creating a SelectNode for proper resolution.
|
||||
rootNode.setOutputSmap(outputSmap);
|
||||
rootNode.setOutputSmap(outputSmap, analyzer);
|
||||
if (rootNode instanceof UnionNode && ((UnionNode) rootNode).isConstantUnion()) {
|
||||
rootNode.setWithoutTupleIsNullOutputSmap(outputSmap);
|
||||
}
|
||||
|
||||
@ -11,3 +11,6 @@
|
||||
3 3 \N \N
|
||||
4 4 \N \N
|
||||
|
||||
-- !select_with_agg_in_inline_view_and_outer_join --
|
||||
1
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
-- !join5 --
|
||||
A p 2 -1
|
||||
B q 0 -1
|
||||
C \N 0 -1
|
||||
C \N \N \N
|
||||
|
||||
-- !join5 --
|
||||
1 1 1 1
|
||||
|
||||
@ -69,4 +69,93 @@ suite("test_outer_join_with_inline_view") {
|
||||
on a.k1 = b.k1
|
||||
order by a.v1;
|
||||
"""
|
||||
|
||||
sql """
|
||||
drop table if exists subquery_table_1;
|
||||
"""
|
||||
|
||||
sql """
|
||||
drop table if exists subquery_table_2;
|
||||
"""
|
||||
|
||||
sql """
|
||||
drop table if exists subquery_table_3;
|
||||
"""
|
||||
|
||||
sql """
|
||||
CREATE TABLE `subquery_table_1` (
|
||||
`org_code` varchar(96) NULL
|
||||
) ENGINE=OLAP
|
||||
DUPLICATE KEY(`org_code`)
|
||||
COMMENT 'OLAP'
|
||||
DISTRIBUTED BY HASH(`org_code`) BUCKETS 2
|
||||
PROPERTIES (
|
||||
"replication_allocation" = "tag.location.default: 1",
|
||||
"in_memory" = "false",
|
||||
"storage_format" = "V2",
|
||||
"disable_auto_compaction" = "false"
|
||||
);
|
||||
"""
|
||||
|
||||
sql """
|
||||
CREATE TABLE `subquery_table_2` (
|
||||
`SKU_CODE` varchar(96) NULL
|
||||
) ENGINE=OLAP
|
||||
DUPLICATE KEY(`SKU_CODE`)
|
||||
COMMENT 'OLAP'
|
||||
DISTRIBUTED BY HASH(`SKU_CODE`) BUCKETS 2
|
||||
PROPERTIES (
|
||||
"replication_allocation" = "tag.location.default: 1",
|
||||
"in_memory" = "false",
|
||||
"storage_format" = "V2",
|
||||
"disable_auto_compaction" = "false"
|
||||
);
|
||||
"""
|
||||
|
||||
sql """
|
||||
CREATE TABLE `subquery_table_3` (
|
||||
`org_code` varchar(96) NULL,
|
||||
`bo_ql_in_advance` decimal(27, 6) NULL
|
||||
) ENGINE=OLAP
|
||||
DUPLICATE KEY(`org_code`)
|
||||
COMMENT 'OLAP'
|
||||
DISTRIBUTED BY HASH(`org_code`) BUCKETS 2
|
||||
PROPERTIES (
|
||||
"replication_allocation" = "tag.location.default: 1",
|
||||
"in_memory" = "false",
|
||||
"storage_format" = "V2",
|
||||
"disable_auto_compaction" = "false"
|
||||
);
|
||||
"""
|
||||
|
||||
sql """
|
||||
insert into subquery_table_1 values('1');
|
||||
"""
|
||||
|
||||
sql """
|
||||
insert into subquery_table_2 values('1');
|
||||
"""
|
||||
|
||||
sql """
|
||||
insert into subquery_table_3 values('1',1);
|
||||
"""
|
||||
|
||||
qt_select_with_agg_in_inline_view_and_outer_join """
|
||||
select
|
||||
count(cc.qlnm) as qlnm
|
||||
FROM
|
||||
subquery_table_1 aa
|
||||
left join (
|
||||
SELECT
|
||||
`s`.`SKU_CODE` AS `org_code`,
|
||||
coalesce(`t3`.`bo_ql_in_advance`, 0) AS `qlnm`
|
||||
FROM
|
||||
`subquery_table_2` s
|
||||
inner JOIN
|
||||
`subquery_table_3` t3
|
||||
ON `s`.`SKU_CODE` = `t3`.`org_code`
|
||||
) cc on aa.org_code = cc.org_code
|
||||
group by
|
||||
aa.org_code;
|
||||
"""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user