diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushCountIntoUnionAll.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushCountIntoUnionAll.java index ddca8e479a3548..9e58f356286658 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushCountIntoUnionAll.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushCountIntoUnionAll.java @@ -110,7 +110,8 @@ private Plan doPush(LogicalAggregate agg) { List newLogicalUnionOutputs = Lists.newArrayList(); for (NamedExpression ce : upperOutputExpressions) { if (ce instanceof Alias) { - newLogicalUnionOutputs.add(new SlotReference(ce.getName(), ce.getDataType(), ce.nullable())); + newLogicalUnionOutputs.add(new SlotReference(ce.getName(), ce.getDataType(), ce.nullable(), + ce.isNameFromChild())); } else if (ce instanceof SlotReference) { newLogicalUnionOutputs.add(ce); } else { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java index 9eea3afd879e67..858c311781b5a6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java @@ -51,6 +51,10 @@ public Alias(Expression child, String name) { this(StatementScopeIdGenerator.newExprId(), child, name, false); } + public Alias(Expression child, String name, boolean nameFromChild) { + this(StatementScopeIdGenerator.newExprId(), child, name, nameFromChild); + } + public Alias(Expression child) { this(StatementScopeIdGenerator.newExprId(), ImmutableList.of(child), Suppliers.memoize(child::toSql), ImmutableList.of(), true); @@ -99,7 +103,8 @@ public Slot toSlot() throws UnboundException { internalName, slotReference != null ? slotReference.getSubPath() - : ImmutableList.of(), Optional.empty() + : ImmutableList.of(), Optional.empty(), + nameFromChild ); } @@ -168,6 +173,7 @@ public R accept(ExpressionVisitor visitor, C context) { return visitor.visitAlias(this, context); } + @Override public boolean isNameFromChild() { return nameFromChild; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ArrayItemReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ArrayItemReference.java index c54ad358561d8e..03c667d46330f8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ArrayItemReference.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ArrayItemReference.java @@ -144,7 +144,7 @@ public static class ArrayItemSlot extends SlotReference implements SlotNotFromCh */ public ArrayItemSlot(ExprId exprId, String name, DataType dataType, boolean nullable) { super(exprId, name, dataType, nullable, ImmutableList.of(), - null, null, Optional.empty(), ImmutableList.of()); + null, null, Optional.empty(), ImmutableList.of(), false); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/MarkJoinSlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/MarkJoinSlotReference.java index e840a32b7c6057..dd67f3b869d882 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/MarkJoinSlotReference.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/MarkJoinSlotReference.java @@ -30,12 +30,12 @@ public class MarkJoinSlotReference extends SlotReference { final boolean existsHasAgg; public MarkJoinSlotReference(String name) { - super(name, BooleanType.INSTANCE, true); + super(name, BooleanType.INSTANCE, true, false); this.existsHasAgg = false; } public MarkJoinSlotReference(String name, boolean existsHasAgg) { - super(name, BooleanType.INSTANCE, true); + super(name, BooleanType.INSTANCE, true, false); this.existsHasAgg = existsHasAgg; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java index d03669234cddcc..30dc76e39c691d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java @@ -48,6 +48,10 @@ public List getQualifier() throws UnboundException { throw new UnboundException("qualifier"); } + public boolean isNameFromChild() { + return false; + } + /** * Get qualified name of NamedExpression. * diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java index e90bc3a5ecfaf4..5b763f86857c66 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java @@ -56,41 +56,47 @@ public class SlotReference extends Slot { private final TableIf table; private final Column column; + private final boolean nameFromChild; public SlotReference(String name, DataType dataType) { this(StatementScopeIdGenerator.newExprId(), name, dataType, true, ImmutableList.of(), - null, null, Optional.empty(), ImmutableList.of()); + null, null, Optional.empty(), ImmutableList.of(), false); } - public SlotReference(String name, DataType dataType, boolean nullable) { + public SlotReference(String name, DataType dataType, boolean nullable, boolean nameFromChild) { this(StatementScopeIdGenerator.newExprId(), name, dataType, nullable, ImmutableList.of(), - null, null, Optional.empty(), ImmutableList.of()); + null, null, Optional.empty(), ImmutableList.of(), nameFromChild); } public SlotReference(String name, DataType dataType, boolean nullable, List qualifier) { this(StatementScopeIdGenerator.newExprId(), name, dataType, nullable, - qualifier, null, null, Optional.empty(), ImmutableList.of()); + qualifier, null, null, Optional.empty(), ImmutableList.of(), + false); } public SlotReference(ExprId exprId, String name, DataType dataType, boolean nullable, List qualifier) { - this(exprId, name, dataType, nullable, qualifier, null, null, Optional.empty(), ImmutableList.of()); + this(exprId, name, dataType, nullable, qualifier, null, null, Optional.empty(), + ImmutableList.of(), false); } public SlotReference(ExprId exprId, String name, DataType dataType, boolean nullable, List qualifier, @Nullable TableIf table, @Nullable Column column) { - this(exprId, name, dataType, nullable, qualifier, table, column, Optional.empty(), ImmutableList.of()); + this(exprId, name, dataType, nullable, qualifier, table, column, Optional.empty(), ImmutableList.of(), + false); } public SlotReference(ExprId exprId, String name, DataType dataType, boolean nullable, List qualifier, @Nullable TableIf table, @Nullable Column column, Optional internalName) { - this(exprId, name, dataType, nullable, qualifier, table, column, internalName, ImmutableList.of()); + this(exprId, name, dataType, nullable, qualifier, table, column, internalName, ImmutableList.of(), + false); } public SlotReference(ExprId exprId, String name, DataType dataType, boolean nullable, List qualifier, @Nullable TableIf table, @Nullable Column column, - Optional internalName, List subColLabels) { + Optional internalName, List subColLabels, boolean nameFromChild) { this(exprId, () -> name, dataType, nullable, qualifier, table, column, - buildInternalName(() -> name, subColLabels, internalName), subColLabels, Optional.empty()); + buildInternalName(() -> name, subColLabels, internalName), subColLabels, Optional.empty(), + nameFromChild); } /** @@ -108,7 +114,8 @@ public SlotReference(ExprId exprId, String name, DataType dataType, boolean null public SlotReference(ExprId exprId, Supplier name, DataType dataType, boolean nullable, List qualifier, @Nullable TableIf table, @Nullable Column column, Supplier> internalName, List subPath, - Optional> indexInSql) { + Optional> indexInSql, + boolean nameFromChild) { super(indexInSql); this.exprId = exprId; this.name = name; @@ -120,6 +127,7 @@ public SlotReference(ExprId exprId, Supplier name, DataType dataType, bo this.column = column; this.subPath = Objects.requireNonNull(subPath, "subPath can not be null"); this.internalName = internalName; + this.nameFromChild = nameFromChild; } public static SlotReference of(String name, DataType type) { @@ -135,13 +143,15 @@ public static SlotReference fromColumn(TableIf table, Column column, List Optional.of(column.getName()), ImmutableList.of(), Optional.empty()); + () -> Optional.of(column.getName()), ImmutableList.of(), Optional.empty(), + false); } public static SlotReference fromColumn(TableIf table, Column column, String name, List qualifier) { DataType dataType = DataType.fromCatalogType(column.getType()); return new SlotReference(StatementScopeIdGenerator.newExprId(), name, dataType, - column.isAllowNull(), qualifier, table, column, Optional.empty(), ImmutableList.of()); + column.isAllowNull(), qualifier, table, column, Optional.empty(), ImmutableList.of(), + false); } @Override @@ -259,7 +269,7 @@ public SlotReference withNullable(boolean nullable) { return this; } return new SlotReference(exprId, name, dataType, nullable, - qualifier, table, column, internalName, subPath, indexInSqlString); + qualifier, table, column, internalName, subPath, indexInSqlString, nameFromChild); } @Override @@ -268,13 +278,13 @@ public Slot withNullableAndDataType(boolean nullable, DataType dataType) { return this; } return new SlotReference(exprId, name, dataType, nullable, - qualifier, table, column, internalName, subPath, indexInSqlString); + qualifier, table, column, internalName, subPath, indexInSqlString, nameFromChild); } @Override public SlotReference withQualifier(List qualifier) { return new SlotReference(exprId, name, dataType, nullable, qualifier, table, column, internalName, subPath, - indexInSqlString); + indexInSqlString, nameFromChild); } @Override @@ -284,29 +294,29 @@ public SlotReference withName(String name) { } return new SlotReference( exprId, () -> name, dataType, nullable, qualifier, table, column, internalName, subPath, - indexInSqlString); + indexInSqlString, nameFromChild); } @Override public SlotReference withExprId(ExprId exprId) { return new SlotReference(exprId, name, dataType, nullable, qualifier, table, column, internalName, subPath, - indexInSqlString); + indexInSqlString, nameFromChild); } public SlotReference withSubPath(List subPath) { return new SlotReference(exprId, name, dataType, !subPath.isEmpty() || nullable, - qualifier, table, column, internalName, subPath, indexInSqlString); + qualifier, table, column, internalName, subPath, indexInSqlString, nameFromChild); } @Override public Slot withIndexInSql(Pair index) { return new SlotReference(exprId, name, dataType, nullable, qualifier, table, column, internalName, subPath, - Optional.ofNullable(index)); + Optional.ofNullable(index), nameFromChild); } public SlotReference withColumn(Column column) { return new SlotReference(exprId, name, dataType, nullable, qualifier, table, column, internalName, subPath, - indexInSqlString); + indexInSqlString, nameFromChild); } public boolean isVisible() { @@ -337,4 +347,9 @@ private static Supplier> buildInternalName( public String getQualifiedNameWithBackquote() throws UnboundException { return Utils.qualifiedNameWithBackquote(getQualifier(), getName()); } + + @Override + public boolean isNameFromChild() { + return nameFromChild; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java index 76a3c36ffe5e9e..8ad31387750194 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java @@ -95,7 +95,7 @@ private AggregateFunction buildForEach(String nestedName, List "foreach must be input array type: '" + nestedName); } DataType itemType = ((ArrayType) arrayType).getItemType(); - return new SlotReference("mocked", itemType, (((ArrayType) arrayType).containsNull())); + return new SlotReference("mocked", itemType, (((ArrayType) arrayType).containsNull()), false); }).collect(Collectors.toList()); return (AggregateFunction) nestedBuilder.build(nestedName, forEachargs).first; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java index 2e4ddb55ff2f02..5893b8c7926824 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java @@ -116,7 +116,8 @@ public List buildNewOutputs() { List slots = resetNullableForLeftOutputs(); ImmutableList.Builder newOutputs = ImmutableList.builderWithExpectedSize(slots.size()); for (Slot slot : slots) { - newOutputs.add(new SlotReference(slot.toSql(), slot.getDataType(), slot.nullable())); + newOutputs.add(new SlotReference(slot.toSql(), slot.getDataType(), slot.nullable(), + slot.isNameFromChild())); } return newOutputs.build(); } @@ -151,10 +152,10 @@ private List> castCommonDataTypeOutputs() { Expression newLeft = TypeCoercionUtils.castIfNotSameTypeStrict(left, compatibleType); Expression newRight = TypeCoercionUtils.castIfNotSameTypeStrict(right, compatibleType); if (newLeft instanceof Cast) { - newLeft = new Alias(newLeft, left.getName()); + newLeft = new Alias(newLeft, left.getName(), left.isNameFromChild()); } if (newRight instanceof Cast) { - newRight = new Alias(newRight, right.getName()); + newRight = new Alias(newRight, right.getName(), right.isNameFromChild()); } newLeftOutputs.add((NamedExpression) newLeft); newRightOutputs.add((NamedExpression) newRight); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/InferPlanOutputAlias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/InferPlanOutputAlias.java index 88f3027f7ac43b..f60105eaf6ad38 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/InferPlanOutputAlias.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/InferPlanOutputAlias.java @@ -22,15 +22,21 @@ import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.logical.LogicalSetOperation; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -38,6 +44,8 @@ */ public class InferPlanOutputAlias { + public static final Logger LOG = LogManager.getLogger(InferPlanOutputAlias.class); + private final List currentOutputs; private final List finalOutputs; private final Set shouldProcessOutputIndex; @@ -55,11 +63,26 @@ public InferPlanOutputAlias(List currentOutputs) { /** infer */ public List infer(Plan plan, ImmutableMultimap currentExprIdAndIndexMap) { ImmutableSet currentOutputExprIdSet = currentExprIdAndIndexMap.keySet(); + Map childToParentMapping = new HashMap<>(); // Breath First Search plan.foreachBreath(childPlan -> { if (shouldProcessOutputIndex.isEmpty()) { return true; } + if (childPlan instanceof LogicalSetOperation) { + List regularChildOutputs = ((LogicalSetOperation) childPlan).getRegularChildOutput(0); + List currentOutputs = ((LogicalSetOperation) childPlan).getOutputs(); + if (regularChildOutputs.size() != currentOutputs.size()) { + // set current output size is different from currentOutputs, not excepted + LOG.error("InferPlanOutputAlias infer regularChildOutputs is different from currentOutputs," + + "child plan is {}", ((LogicalSetOperation) childPlan).treeString()); + return true; + } + for (int index = 0; index < regularChildOutputs.size(); index++) { + childToParentMapping.put(regularChildOutputs.get(index).getExprId(), + currentOutputs.get(index).getExprId()); + } + } for (Expression expression : ((Plan) childPlan).getExpressions()) { if (!(expression instanceof Alias)) { continue; @@ -67,10 +90,11 @@ public List infer(Plan plan, ImmutableMultimap Alias projectItem = (Alias) expression; ExprId exprId = projectItem.getExprId(); // Infer name when alias child is expression and alias's name is from child - if (currentOutputExprIdSet.contains(projectItem.getExprId()) - && projectItem.isNameFromChild()) { + if (contains(currentOutputExprIdSet, exprId, childToParentMapping) + && (projectItem.isNameFromChild())) { String inferredAliasName = projectItem.child().getExpressionName(); - ImmutableCollection outputExprIndexes = currentExprIdAndIndexMap.get(exprId); + ImmutableCollection outputExprIndexes = getOutputSlotIndex(currentExprIdAndIndexMap, + exprId, childToParentMapping); // replace output name by inferred name for (Integer index : outputExprIndexes) { Slot slot = currentOutputs.get(index); @@ -89,4 +113,50 @@ public List infer(Plan plan, ImmutableMultimap }); return finalOutputs; } + + /** + * Such as LogicalIntersect, targetExprIdSet is ['back'#41, 'we'#42], but child is ['back'#38, 'we'#40] + * should construct childToParentMapping { + * 'back'#38 : 'back'#41, + * 'we'#40 : 'we'#42 + * } + * LogicalIntersect ( qualifier=DISTINCT, + * outputs=['back'#41, 'we'#42], + * regularChildrenOutputs=[['back'#38, 'we'#40], + * [col_char_25__undef_signed#39, col_varchar_25__undef_signed_not_null#27]] ) + */ + private static boolean contains(ImmutableSet targetExprIdSet, ExprId exprId, + Map childToParentMapping) { + if (targetExprIdSet.contains(exprId)) { + return true; + } + return targetExprIdSet.contains(childToParentMapping.get(exprId)); + } + + /** + * Such as LogicalIntersect, currentExprIdAndIndexMap is + * { + * 'back'#38 : [0], + * 'we'#40 : [1] + * } + * LogicalIntersect ( qualifier=DISTINCT, + * outputs=['back'#41, 'we'#42], + * regularChildrenOutputs=[['back'#38, 'we'#40], + * [col_char_25__undef_signed#39, col_varchar_25__undef_signed_not_null#27]] ) + * exprId is 'back'#38 + * childToParentMapping { + * 'back'#38 : 'back'#41, + * 'we'#40 : 'we'#42 + * } + */ + private static ImmutableCollection getOutputSlotIndex( + ImmutableMultimap currentExprIdAndIndexMap, + ExprId exprId, + Map childToParentMapping) { + ImmutableCollection indexes = currentExprIdAndIndexMap.get(exprId); + if (!indexes.isEmpty()) { + return indexes; + } + return currentExprIdAndIndexMap.get(childToParentMapping.get(exprId)); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/AggStateType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/AggStateType.java index f3f986bc26d233..4be1a8bc2919d9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/AggStateType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/AggStateType.java @@ -80,7 +80,7 @@ public AggStateType(String functionName, List subTypes, List public List getMockedExpressions() { List result = new ArrayList(); for (int i = 0; i < subTypes.size(); i++) { - result.add(new SlotReference("mocked", subTypes.get(i), subTypeNullables.get(i))); + result.add(new SlotReference("mocked", subTypes.get(i), subTypeNullables.get(i), false)); } return result; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java index 25637d1b816656..87182596356598 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java @@ -441,7 +441,8 @@ public static NamedExpression replaceNameExpression(NamedExpression expr, if (newExpr instanceof NamedExpression) { return (NamedExpression) newExpr; } else { - return new Alias(expr.getExprId(), newExpr, expr.getName()); + return new Alias(expr.getExprId(), newExpr, expr.getName(), + expr.isNameFromChild()); } } diff --git a/regression-test/suites/nereids_p0/infer_expr_name/set_operation.groovy b/regression-test/suites/nereids_p0/infer_expr_name/set_operation.groovy new file mode 100644 index 00000000000000..ba893dbc3370f0 --- /dev/null +++ b/regression-test/suites/nereids_p0/infer_expr_name/set_operation.groovy @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +suite("set_operation") { + + def queryResult1 = sql """ + explain analyzed plan + SELECT 'STORE', + date_add(ss_sold_date_sk, INTERVAL 2 DAY), + ss_item_sk as item_sk, + ss_sales_price as sales_price, + 'STORE()##' + FROM store_sales + UNION ALL + SELECT 'WEB', + ws_sold_date_sk as date_sk, + ws_item_sk as item_sk, + ws_sales_price as sales_price, + '' + FROM web_sales + UNION ALL + SELECT 'CATALOG' as channel, + cs_sold_date_sk as date_sk, + cs_item_sk as item_sk, + cs_sales_price as sales_price, + '' + FROM catalog_sales; + """ + + def topPlan1 = queryResult1[0][0].toString() + assertTrue(topPlan1.contains("LogicalResultSink")) + assertTrue(topPlan1.contains("")) + + + def queryResult2 = sql """ + """ + + def topPlan2 = queryResult2[0][0].toString() + assertTrue(topPlan2.contains("LogicalResultSink")) + assertTrue(topPlan2.contains("")) + + + def queryResult3 = sql """ + """ + + def topPlan3 = queryResult3[0][0].toString() + assertTrue(topPlan3.contains("LogicalResultSink")) + assertTrue(topPlan3.contains("")) + +} +