[fix](nereids) AdjustNullable rule should handle union node with no children (#35074)
The output slot's nullable info is not correctly calculated in union node. Because old code only get correct result if union node has children. But the union node may have no children but only have constantExprList. So in that case, we should calculate output's nullable info byboth children and constantExprList.
This commit is contained in:
@ -165,45 +165,55 @@ public class AdjustNullable extends DefaultPlanRewriter<Map<ExprId, Slot>> imple
|
||||
@Override
|
||||
public Plan visitLogicalSetOperation(LogicalSetOperation setOperation, Map<ExprId, Slot> replaceMap) {
|
||||
setOperation = (LogicalSetOperation) super.visit(setOperation, replaceMap);
|
||||
if (setOperation.children().isEmpty()) {
|
||||
return setOperation;
|
||||
}
|
||||
List<Boolean> inputNullable = setOperation.child(0).getOutput().stream()
|
||||
.map(ExpressionTrait::nullable).collect(Collectors.toList());
|
||||
ImmutableList.Builder<List<SlotReference>> newChildrenOutputs = ImmutableList.builder();
|
||||
for (int i = 0; i < setOperation.arity(); i++) {
|
||||
List<Slot> childOutput = setOperation.child(i).getOutput();
|
||||
List<SlotReference> setChildOutput = setOperation.getRegularChildOutput(i);
|
||||
ImmutableList.Builder<SlotReference> newChildOutputs = ImmutableList.builder();
|
||||
for (int j = 0; j < setChildOutput.size(); j++) {
|
||||
for (Slot slot : childOutput) {
|
||||
if (slot.getExprId().equals(setChildOutput.get(j).getExprId())) {
|
||||
inputNullable.set(j, slot.nullable() || inputNullable.get(j));
|
||||
newChildOutputs.add((SlotReference) slot);
|
||||
break;
|
||||
List<Boolean> inputNullable = null;
|
||||
if (!setOperation.children().isEmpty()) {
|
||||
inputNullable = setOperation.child(0).getOutput().stream()
|
||||
.map(ExpressionTrait::nullable).collect(Collectors.toList());
|
||||
for (int i = 0; i < setOperation.arity(); i++) {
|
||||
List<Slot> childOutput = setOperation.child(i).getOutput();
|
||||
List<SlotReference> setChildOutput = setOperation.getRegularChildOutput(i);
|
||||
ImmutableList.Builder<SlotReference> newChildOutputs = ImmutableList.builder();
|
||||
for (int j = 0; j < setChildOutput.size(); j++) {
|
||||
for (Slot slot : childOutput) {
|
||||
if (slot.getExprId().equals(setChildOutput.get(j).getExprId())) {
|
||||
inputNullable.set(j, slot.nullable() || inputNullable.get(j));
|
||||
newChildOutputs.add((SlotReference) slot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
newChildrenOutputs.add(newChildOutputs.build());
|
||||
}
|
||||
newChildrenOutputs.add(newChildOutputs.build());
|
||||
}
|
||||
if (setOperation instanceof LogicalUnion) {
|
||||
LogicalUnion logicalUnion = (LogicalUnion) setOperation;
|
||||
for (List<NamedExpression> constantExprs : logicalUnion.getConstantExprsList()) {
|
||||
for (int j = 0; j < constantExprs.size(); j++) {
|
||||
if (constantExprs.get(j).nullable()) {
|
||||
inputNullable.set(j, true);
|
||||
}
|
||||
if (!logicalUnion.getConstantExprsList().isEmpty() && setOperation.children().isEmpty()) {
|
||||
int outputSize = logicalUnion.getConstantExprsList().get(0).size();
|
||||
// create the inputNullable list and fill it with all FALSE values
|
||||
inputNullable = Lists.newArrayListWithCapacity(outputSize);
|
||||
for (int i = 0; i < outputSize; i++) {
|
||||
inputNullable.add(false);
|
||||
}
|
||||
}
|
||||
for (List<NamedExpression> constantExprs : logicalUnion.getConstantExprsList()) {
|
||||
for (int j = 0; j < constantExprs.size(); j++) {
|
||||
inputNullable.set(j, inputNullable.get(j) || constantExprs.get(j).nullable());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inputNullable == null) {
|
||||
// this is a fail-safe
|
||||
// means there is no children and having no getConstantExprsList
|
||||
// no way to update the nullable flag, so just do nothing
|
||||
return setOperation;
|
||||
}
|
||||
List<NamedExpression> outputs = setOperation.getOutputs();
|
||||
List<NamedExpression> newOutputs = Lists.newArrayListWithCapacity(outputs.size());
|
||||
for (int i = 0; i < inputNullable.size(); i++) {
|
||||
NamedExpression ne = outputs.get(i);
|
||||
Slot slot = ne instanceof Alias ? (Slot) ((Alias) ne).child() : (Slot) ne;
|
||||
if (inputNullable.get(i)) {
|
||||
slot = slot.withNullable(true);
|
||||
}
|
||||
slot = slot.withNullable(inputNullable.get(i));
|
||||
newOutputs.add(ne instanceof Alias ? (NamedExpression) ne.withChildren(slot) : slot);
|
||||
}
|
||||
newOutputs.forEach(o -> replaceMap.put(o.getExprId(), o.toSlot()));
|
||||
|
||||
@ -67,9 +67,8 @@ public class PushProjectIntoUnion extends OneRewriteRuleFactory {
|
||||
}
|
||||
newConstExprs.add(newProjections.build());
|
||||
}
|
||||
return p.child()
|
||||
.withChildrenAndConstExprsList(ImmutableList.of(), ImmutableList.of(), newConstExprs.build())
|
||||
.withNewOutputs(p.getOutputs());
|
||||
return p.child().withNewOutputsChildrenAndConstExprsList(ImmutableList.copyOf(p.getOutput()),
|
||||
ImmutableList.of(), ImmutableList.of(), newConstExprs.build());
|
||||
}).toRule(RuleType.PUSH_PROJECT_INTO_UNION);
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,6 +180,13 @@ public class LogicalUnion extends LogicalSetOperation implements Union, OutputPr
|
||||
return new LogicalUnion(qualifier, outputs, childrenOutputs, constantExprsList, hasPushedFilter, children);
|
||||
}
|
||||
|
||||
public LogicalUnion withNewOutputsChildrenAndConstExprsList(List<NamedExpression> newOutputs, List<Plan> children,
|
||||
List<List<SlotReference>> childrenOutputs,
|
||||
List<List<NamedExpression>> constantExprsList) {
|
||||
return new LogicalUnion(qualifier, newOutputs, childrenOutputs, constantExprsList,
|
||||
hasPushedFilter, Optional.empty(), Optional.empty(), children);
|
||||
}
|
||||
|
||||
public LogicalUnion withAllQualifier() {
|
||||
return new LogicalUnion(Qualifier.ALL, outputs, regularChildrenOutputs, constantExprsList, hasPushedFilter,
|
||||
Optional.empty(), Optional.empty(), children);
|
||||
|
||||
@ -95,5 +95,23 @@ suite("adjust_nullable") {
|
||||
sql """
|
||||
drop table if exists table_8_undef_undef;
|
||||
"""
|
||||
|
||||
sql """
|
||||
drop table if exists orders_2_x;
|
||||
"""
|
||||
|
||||
sql """CREATE TABLE `orders_2_x` (
|
||||
`o_orderdate` DATE not NULL
|
||||
) ENGINE=OLAP
|
||||
DUPLICATE KEY(`o_orderdate`)
|
||||
DISTRIBUTED BY HASH(`o_orderdate`) BUCKETS 96
|
||||
PROPERTIES (
|
||||
"replication_allocation" = "tag.location.default: 1"
|
||||
);"""
|
||||
|
||||
explain {
|
||||
sql("verbose insert into orders_2_x values ( '2023-10-17'),( '2023-10-17');")
|
||||
notContains("nullable=true")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user