[fix](Nereids) null type in result set will be cast to tinyint (#37019) (#37281)

pick from master #37019
This commit is contained in:
morrySnow
2024-07-04 19:18:35 +08:00
committed by GitHub
parent b272247a57
commit e4fb506c20
4 changed files with 100 additions and 8 deletions

View File

@ -42,6 +42,7 @@ import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.BoundStar;
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.DefaultValueSlot;
import org.apache.doris.nereids.trees.expressions.EqualTo;
import org.apache.doris.nereids.trees.expressions.ExprId;
@ -89,8 +90,11 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalTVFRelation;
import org.apache.doris.nereids.trees.plans.logical.UsingJoin;
import org.apache.doris.nereids.trees.plans.visitor.InferPlanOutputAlias;
import org.apache.doris.nereids.types.BooleanType;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.NullType;
import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import org.apache.doris.nereids.types.TinyIntType;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.PlanUtils;
import org.apache.doris.nereids.util.TypeCoercionUtils;
@ -208,22 +212,38 @@ public class BindExpression implements AnalysisRuleFactory {
private LogicalResultSink<Plan> bindResultSink(MatchingContext<UnboundResultSink<Plan>> ctx) {
LogicalSink<Plan> sink = ctx.root;
Plan child = sink.child();
List<Slot> output = child.getOutput();
List<NamedExpression> castNullToTinyInt = Lists.newArrayListWithCapacity(output.size());
boolean needProject = false;
for (Slot slot : output) {
DataType newType = TypeCoercionUtils.replaceSpecifiedType(
slot.getDataType(), NullType.class, TinyIntType.INSTANCE);
if (!newType.equals(slot.getDataType())) {
needProject = true;
castNullToTinyInt.add(new Alias(new Cast(slot, newType)));
} else {
castNullToTinyInt.add(slot);
}
}
if (needProject) {
child = new LogicalProject<>(castNullToTinyInt, child);
}
if (ctx.connectContext.getState().isQuery()) {
List<NamedExpression> outputExprs = sink.child().getOutput().stream()
List<NamedExpression> outputExprs = child.getOutput().stream()
.map(NamedExpression.class::cast)
.collect(ImmutableList.toImmutableList());
return new LogicalResultSink<>(outputExprs, sink.child());
return new LogicalResultSink<>(outputExprs, child);
}
// Should infer column name for expression when query command
final ImmutableListMultimap.Builder<ExprId, Integer> exprIdToIndexMapBuilder =
ImmutableListMultimap.builder();
List<Slot> childOutput = sink.child().getOutput();
final ImmutableListMultimap.Builder<ExprId, Integer> exprIdToIndexMapBuilder = ImmutableListMultimap.builder();
List<Slot> childOutput = child.getOutput();
for (int index = 0; index < childOutput.size(); index++) {
exprIdToIndexMapBuilder.put(childOutput.get(index).getExprId(), index);
}
InferPlanOutputAlias aliasInfer = new InferPlanOutputAlias(childOutput);
List<NamedExpression> output = aliasInfer.infer(sink.child(), exprIdToIndexMapBuilder.build());
return new LogicalResultSink<>(output, sink.child());
List<NamedExpression> sinkExpr = aliasInfer.infer(child, exprIdToIndexMapBuilder.build());
return new LogicalResultSink<>(sinkExpr, child);
}
private LogicalSubQueryAlias<Plan> bindSubqueryAlias(MatchingContext<LogicalSubQueryAlias<Plan>> ctx) {

View File

@ -341,7 +341,8 @@ public class TypeCoercionUtils {
public static DataType replaceSpecifiedType(DataType dataType,
Class<? extends DataType> specifiedType, DataType newType) {
if (dataType instanceof ArrayType) {
return ArrayType.of(replaceSpecifiedType(((ArrayType) dataType).getItemType(), specifiedType, newType));
return ArrayType.of(replaceSpecifiedType(((ArrayType) dataType).getItemType(), specifiedType, newType),
((ArrayType) dataType).containsNull());
} else if (dataType instanceof MapType) {
return MapType.of(replaceSpecifiedType(((MapType) dataType).getKeyType(), specifiedType, newType),
replaceSpecifiedType(((MapType) dataType).getValueType(), specifiedType, newType));