[feature](Nereids) Support OneRowRelation and EmptyRelation (#12416)
Support OneRowRelation and EmptyRelation.
OneRowRelation: `select 100, 'abc', substring('abc', 1, 2)`
EmptyRelation: `select * from tbl limit 0`
Note:
PhysicalOneRowRelation will translate to UnionNode(constExpr) for BE execution
This commit is contained in:
@ -47,6 +47,14 @@ public class Pair<F, S> {
|
||||
return new Pair<>(first, second);
|
||||
}
|
||||
|
||||
public F key() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public S value() {
|
||||
return second;
|
||||
}
|
||||
|
||||
/**
|
||||
* A pair is equal if both parts are equal().
|
||||
*/
|
||||
|
||||
@ -111,7 +111,7 @@ public class NereidsPlanner extends Planner {
|
||||
// TODO: What is the appropriate time to set physical properties? Maybe before enter.
|
||||
// cascades style optimize phase.
|
||||
|
||||
// cost-based optimize and explode plan space
|
||||
// cost-based optimize and explore plan space
|
||||
optimize();
|
||||
|
||||
PhysicalPlan physicalPlan = chooseBestPlan(getRoot(), PhysicalProperties.ANY);
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.analyzer;
|
||||
|
||||
/**
|
||||
* Relation base interface
|
||||
*/
|
||||
public interface Relation {
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.analyzer;
|
||||
|
||||
import org.apache.doris.nereids.exceptions.UnboundException;
|
||||
import org.apache.doris.nereids.memo.GroupExpression;
|
||||
import org.apache.doris.nereids.properties.LogicalProperties;
|
||||
import org.apache.doris.nereids.properties.UnboundLogicalProperties;
|
||||
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.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.OneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalLeaf;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
|
||||
import org.apache.doris.nereids.util.Utils;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A relation that contains only one row consist of some constant expressions.
|
||||
* e.g. select 100, 'value'
|
||||
*/
|
||||
public class UnboundOneRowRelation extends LogicalLeaf implements Unbound, OneRowRelation {
|
||||
private final List<NamedExpression> projects;
|
||||
|
||||
public UnboundOneRowRelation(List<NamedExpression> projects) {
|
||||
this(projects, Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
private UnboundOneRowRelation(List<NamedExpression> projects, Optional<GroupExpression> groupExpression,
|
||||
Optional<LogicalProperties> logicalProperties) {
|
||||
super(PlanType.LOGICAL_UNBOUND_ONE_ROW_RELATION, groupExpression, logicalProperties);
|
||||
Preconditions.checkArgument(projects.stream().noneMatch(p -> p.containsType(Slot.class)),
|
||||
"OneRowRelation can not contains any slot");
|
||||
this.projects = ImmutableList.copyOf(projects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitUnboundOneRowRelation(this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NamedExpression> getProjects() {
|
||||
return projects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Expression> getExpressions() {
|
||||
throw new UnsupportedOperationException(this.getClass().getSimpleName() + " don't support getExpression()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
|
||||
return new UnboundOneRowRelation(projects, groupExpression, Optional.of(logicalPropertiesSupplier.get()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
|
||||
return new UnboundOneRowRelation(projects, Optional.empty(), logicalProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Slot> computeOutput() {
|
||||
throw new UnboundException("output");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogicalProperties computeLogicalProperties() {
|
||||
return UnboundLogicalProperties.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Utils.toSqlString("UnboundOneRowRelation",
|
||||
"projects", projects
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
UnboundOneRowRelation that = (UnboundOneRowRelation) o;
|
||||
return Objects.equals(projects, that.projects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(projects);
|
||||
}
|
||||
}
|
||||
@ -40,7 +40,7 @@ import java.util.Optional;
|
||||
/**
|
||||
* Represent a relation plan node that has not been bound.
|
||||
*/
|
||||
public class UnboundRelation extends LogicalLeaf implements Unbound {
|
||||
public class UnboundRelation extends LogicalLeaf implements Relation, Unbound {
|
||||
private final List<String> nameParts;
|
||||
|
||||
public UnboundRelation(List<String> nameParts) {
|
||||
@ -103,7 +103,9 @@ public class UnboundRelation extends LogicalLeaf implements Unbound {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UnboundRelation" + "(" + StringUtils.join(nameParts, ".") + ")";
|
||||
return Utils.toSqlString("UnboundRelation",
|
||||
"nameParts", StringUtils.join(nameParts, ".")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -133,6 +135,6 @@ public class UnboundRelation extends LogicalLeaf implements Unbound {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), nameParts);
|
||||
return Objects.hash(nameParts);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@ import org.apache.doris.analysis.SortInfo;
|
||||
import org.apache.doris.analysis.TableName;
|
||||
import org.apache.doris.analysis.TableRef;
|
||||
import org.apache.doris.analysis.TupleDescriptor;
|
||||
import org.apache.doris.analysis.TupleId;
|
||||
import org.apache.doris.catalog.OlapTable;
|
||||
import org.apache.doris.catalog.Table;
|
||||
import org.apache.doris.common.Pair;
|
||||
@ -52,11 +53,13 @@ import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalSort;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribute;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalLimit;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalNestedLoopJoin;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalOneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalQuickSort;
|
||||
@ -69,6 +72,7 @@ import org.apache.doris.planner.AggregationNode;
|
||||
import org.apache.doris.planner.AssertNumRowsNode;
|
||||
import org.apache.doris.planner.CrossJoinNode;
|
||||
import org.apache.doris.planner.DataPartition;
|
||||
import org.apache.doris.planner.EmptySetNode;
|
||||
import org.apache.doris.planner.ExchangeNode;
|
||||
import org.apache.doris.planner.HashJoinNode;
|
||||
import org.apache.doris.planner.HashJoinNode.DistributionMode;
|
||||
@ -76,6 +80,7 @@ import org.apache.doris.planner.OlapScanNode;
|
||||
import org.apache.doris.planner.PlanFragment;
|
||||
import org.apache.doris.planner.PlanNode;
|
||||
import org.apache.doris.planner.SortNode;
|
||||
import org.apache.doris.planner.UnionNode;
|
||||
import org.apache.doris.thrift.TPartitionType;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
@ -227,6 +232,56 @@ public class PhysicalPlanTranslator extends DefaultPlanVisitor<PlanFragment, Pla
|
||||
return currentFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlanFragment visitPhysicalEmptyRelation(PhysicalEmptyRelation emptyRelation, PlanTranslatorContext context) {
|
||||
List<Slot> output = emptyRelation.getOutput();
|
||||
TupleDescriptor tupleDescriptor = generateTupleDesc(output, null, context);
|
||||
for (int i = 0; i < output.size(); i++) {
|
||||
SlotDescriptor slotDescriptor = tupleDescriptor.getSlots().get(i);
|
||||
slotDescriptor.setIsNullable(true); // we should set to nullable, or else BE would core
|
||||
|
||||
Slot slot = output.get(i);
|
||||
SlotRef slotRef = context.findSlotRef(slot.getExprId());
|
||||
slotRef.setLabel(slot.getName());
|
||||
}
|
||||
|
||||
ArrayList<TupleId> tupleIds = new ArrayList();
|
||||
tupleIds.add(tupleDescriptor.getId());
|
||||
EmptySetNode emptySetNode = new EmptySetNode(context.nextPlanNodeId(), tupleIds);
|
||||
|
||||
PlanFragment planFragment = new PlanFragment(context.nextFragmentId(), emptySetNode,
|
||||
DataPartition.UNPARTITIONED);
|
||||
context.addPlanFragment(planFragment);
|
||||
return planFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlanFragment visitPhysicalOneRowRelation(PhysicalOneRowRelation oneRowRelation,
|
||||
PlanTranslatorContext context) {
|
||||
List<Slot> slots = oneRowRelation.getLogicalProperties().getOutput();
|
||||
TupleDescriptor oneRowTuple = generateTupleDesc(slots, null, context);
|
||||
|
||||
List<Expr> legacyExprs = oneRowRelation.getProjects()
|
||||
.stream()
|
||||
.map(expr -> ExpressionTranslator.translate(expr, context))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (int i = 0; i < legacyExprs.size(); i++) {
|
||||
SlotDescriptor slotDescriptor = oneRowTuple.getSlots().get(i);
|
||||
Expr expr = legacyExprs.get(i);
|
||||
slotDescriptor.setSourceExpr(expr);
|
||||
slotDescriptor.setIsNullable(true); // we should set to nullable, or else BE would core
|
||||
}
|
||||
|
||||
UnionNode unionNode = new UnionNode(context.nextPlanNodeId(), oneRowTuple.getId());
|
||||
unionNode.addConstExprList(legacyExprs);
|
||||
unionNode.finalizeForNereids(oneRowTuple, oneRowTuple.getSlots());
|
||||
|
||||
PlanFragment planFragment = new PlanFragment(context.nextFragmentId(), unionNode, DataPartition.UNPARTITIONED);
|
||||
context.addPlanFragment(planFragment);
|
||||
return planFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlanFragment visitPhysicalOlapScan(PhysicalOlapScan olapScan, PlanTranslatorContext context) {
|
||||
// Create OlapScanNode
|
||||
|
||||
@ -38,6 +38,7 @@ import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Context of physical plan.
|
||||
@ -117,10 +118,10 @@ public class PlanTranslatorContext {
|
||||
*/
|
||||
public SlotDescriptor createSlotDesc(TupleDescriptor tupleDesc, SlotReference slotReference) {
|
||||
SlotDescriptor slotDescriptor = this.addSlotDesc(tupleDesc);
|
||||
Column column = slotReference.getColumn();
|
||||
Optional<Column> column = slotReference.getColumn();
|
||||
// Only the SlotDesc that in the tuple generated for scan node would have corresponding column.
|
||||
if (column != null) {
|
||||
slotDescriptor.setColumn(column);
|
||||
if (column.isPresent()) {
|
||||
slotDescriptor.setColumn(column.get());
|
||||
}
|
||||
slotDescriptor.setType(slotReference.getDataType().toCatalogDataType());
|
||||
slotDescriptor.setIsMaterialized(true);
|
||||
|
||||
@ -23,6 +23,7 @@ import org.apache.doris.nereids.rules.expression.rewrite.ExpressionNormalization
|
||||
import org.apache.doris.nereids.rules.rewrite.AggregateDisassemble;
|
||||
import org.apache.doris.nereids.rules.rewrite.logical.ColumnPruning;
|
||||
import org.apache.doris.nereids.rules.rewrite.logical.FindHashConditionForJoin;
|
||||
import org.apache.doris.nereids.rules.rewrite.logical.LogicalLimitZeroToLogicalEmptyRelation;
|
||||
import org.apache.doris.nereids.rules.rewrite.logical.MergeConsecutiveFilters;
|
||||
import org.apache.doris.nereids.rules.rewrite.logical.MergeConsecutiveLimits;
|
||||
import org.apache.doris.nereids.rules.rewrite.logical.MergeConsecutiveProjects;
|
||||
@ -67,6 +68,7 @@ public class RewriteJob extends BatchRulesJob {
|
||||
.add(bottomUpBatch(ImmutableList.of(new MergeConsecutiveProjects())))
|
||||
.add(topDownBatch(ImmutableList.of(new MergeConsecutiveFilters())))
|
||||
.add(bottomUpBatch(ImmutableList.of(new MergeConsecutiveLimits())))
|
||||
.add(bottomUpBatch(ImmutableList.of(new LogicalLimitZeroToLogicalEmptyRelation())))
|
||||
.add(topDownBatch(ImmutableList.of(new PruneOlapScanPartition())))
|
||||
.build();
|
||||
|
||||
|
||||
@ -18,13 +18,11 @@
|
||||
package org.apache.doris.nereids.memo;
|
||||
|
||||
import org.apache.doris.common.Pair;
|
||||
import org.apache.doris.nereids.analyzer.UnboundRelation;
|
||||
import org.apache.doris.nereids.analyzer.Relation;
|
||||
import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalRelation;
|
||||
import org.apache.doris.statistics.StatsDeriveResult;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
@ -208,10 +206,9 @@ public class GroupExpression {
|
||||
return false;
|
||||
}
|
||||
GroupExpression that = (GroupExpression) o;
|
||||
// if the plan is UnboundRelation or LogicalRelation or PhysicalRelation, this == that should be true,
|
||||
// when if one relation appear in plan more than once,
|
||||
// we cannot distinguish them throw equals function, since equals function cannot use output info.
|
||||
if (plan instanceof UnboundRelation || plan instanceof LogicalRelation || plan instanceof PhysicalRelation) {
|
||||
// FIXME: Doris not support temporary materialization, so we should not merge same
|
||||
// scan relation plan. We should add id for XxxRelation and compare by id.
|
||||
if (plan instanceof Relation) {
|
||||
return false;
|
||||
}
|
||||
return children.equals(that.children) && plan.equals(that.plan)
|
||||
|
||||
@ -158,19 +158,21 @@ public class Memo {
|
||||
* +---------------------------------------+-----------------------------------+--------------------------------+
|
||||
* | case 2: | | |
|
||||
* | if targetGroup is null | true | new group expression |
|
||||
* | and same group expression exist | | |
|
||||
* | and same group expression not exist | | |
|
||||
* +---------------------------------------+-----------------------------------+--------------------------------+
|
||||
* | case 3: | | |
|
||||
* | if targetGroup is null | true | new group expression |
|
||||
* | if targetGroup is not null | true | new group expression |
|
||||
* | and same group expression not exits | | |
|
||||
* +---------------------------------------+-----------------------------------+--------------------------------+
|
||||
* | case 4: | | |
|
||||
* | if targetGroup equal to the exists | true | new group expression |
|
||||
* | group expression's owner group | | |
|
||||
* | if targetGroup is not null and not | true | new group expression |
|
||||
* | equal to the existed group | | |
|
||||
* | expression's owner group | | |
|
||||
* +---------------------------------------+-----------------------------------+--------------------------------+
|
||||
* | case 5: | | |
|
||||
* | if targetGroup not equal to the | false | existed group expression |
|
||||
* | exists group expression's owner group | | |
|
||||
* | if targetGroup is null or equal to | false | existed group expression |
|
||||
* | the existed group expression's owner | | |
|
||||
* | group | | |
|
||||
* +---------------------------------------+-----------------------------------+--------------------------------+
|
||||
* </pre>
|
||||
*
|
||||
@ -190,15 +192,19 @@ public class Memo {
|
||||
return rewriteByExistedPlan(targetGroup, plan);
|
||||
}
|
||||
|
||||
// try to create a new group expression
|
||||
List<Group> childrenGroups = rewriteChildrenPlansToGroups(plan, targetGroup);
|
||||
plan = replaceChildrenToGroupPlan(plan, childrenGroups);
|
||||
|
||||
// try to create a new group expression
|
||||
GroupExpression newGroupExpression = new GroupExpression(plan, childrenGroups);
|
||||
|
||||
// slow check the groupExpression/plan whether exists in the memo
|
||||
GroupExpression existedExpression = groupExpressions.get(newGroupExpression);
|
||||
if (existedExpression == null) {
|
||||
// case 2 or case 3
|
||||
return rewriteByNewGroupExpression(targetGroup, plan, newGroupExpression);
|
||||
} else {
|
||||
// case 4 or case 5
|
||||
return rewriteByExistedGroupExpression(targetGroup, plan, existedExpression, newGroupExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +72,7 @@ import org.apache.doris.nereids.DorisParser.WhereClauseContext;
|
||||
import org.apache.doris.nereids.DorisParserBaseVisitor;
|
||||
import org.apache.doris.nereids.analyzer.UnboundAlias;
|
||||
import org.apache.doris.nereids.analyzer.UnboundFunction;
|
||||
import org.apache.doris.nereids.analyzer.UnboundOneRowRelation;
|
||||
import org.apache.doris.nereids.analyzer.UnboundRelation;
|
||||
import org.apache.doris.nereids.analyzer.UnboundSlot;
|
||||
import org.apache.doris.nereids.analyzer.UnboundStar;
|
||||
@ -117,7 +118,9 @@ import org.apache.doris.nereids.trees.expressions.literal.IntervalLiteral;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.LargeIntLiteral;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.Literal;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
|
||||
import org.apache.doris.nereids.trees.plans.JoinType;
|
||||
import org.apache.doris.nereids.trees.plans.commands.Command;
|
||||
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
|
||||
@ -216,9 +219,8 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
|
||||
@Override
|
||||
public LogicalPlan visitRegularQuerySpecification(RegularQuerySpecificationContext ctx) {
|
||||
return ParserUtils.withOrigin(ctx, () -> {
|
||||
// TODO: support one row relation
|
||||
if (ctx.fromClause() == null) {
|
||||
throw new ParseException("Unsupported one row relation", ctx);
|
||||
return withOneRowRelation(ctx.selectClause());
|
||||
}
|
||||
|
||||
LogicalPlan relation = visitFromClause(ctx.fromClause());
|
||||
@ -588,7 +590,11 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
|
||||
@Override
|
||||
public Literal visitIntegerLiteral(IntegerLiteralContext ctx) {
|
||||
BigInteger bigInt = new BigInteger(ctx.getText());
|
||||
if (BigInteger.valueOf(bigInt.intValue()).equals(bigInt)) {
|
||||
if (BigInteger.valueOf(bigInt.byteValue()).equals(bigInt)) {
|
||||
return new TinyIntLiteral(bigInt.byteValue());
|
||||
} else if (BigInteger.valueOf(bigInt.shortValue()).equals(bigInt)) {
|
||||
return new SmallIntLiteral(bigInt.shortValue());
|
||||
} else if (BigInteger.valueOf(bigInt.intValue()).equals(bigInt)) {
|
||||
return new IntegerLiteral(bigInt.intValue());
|
||||
} else if (BigInteger.valueOf(bigInt.longValue()).equals(bigInt)) {
|
||||
return new BigIntLiteral(bigInt.longValueExact());
|
||||
@ -605,7 +611,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
|
||||
.map(str -> str.substring(1, str.length() - 1))
|
||||
.reduce((s1, s2) -> s1 + s2)
|
||||
.orElse("");
|
||||
return new StringLiteral(s);
|
||||
return new VarcharLiteral(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -749,6 +755,13 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
|
||||
});
|
||||
}
|
||||
|
||||
private UnboundOneRowRelation withOneRowRelation(SelectClauseContext selectCtx) {
|
||||
return ParserUtils.withOrigin(selectCtx, () -> {
|
||||
List<NamedExpression> projects = getNamedExpressions(selectCtx.namedExpressionSeq());
|
||||
return new UnboundOneRowRelation(projects);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a regular (SELECT) query specification to a logical plan. The query specification
|
||||
* is the core of the logical plan, this is where sourcing (FROM clause), projection (SELECT),
|
||||
|
||||
@ -21,11 +21,13 @@ import org.apache.doris.nereids.rules.exploration.join.JoinCommute;
|
||||
import org.apache.doris.nereids.rules.exploration.join.JoinCommuteProject;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalAggToPhysicalHashAgg;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalAssertNumRowsToPhysicalAssertNumRows;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalEmptyRelationToPhysicalEmptyRelation;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalFilterToPhysicalFilter;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalJoinToHashJoin;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalJoinToNestedLoopJoin;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalLimitToPhysicalLimit;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalOlapScanToPhysicalOlapScan;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalOneRowRelationToPhysicalOneRowRelation;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalProjectToPhysicalProject;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalSortToPhysicalQuickSort;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalTopNToPhysicalTopN;
|
||||
@ -60,6 +62,8 @@ public class RuleSet {
|
||||
.add(new LogicalSortToPhysicalQuickSort())
|
||||
.add(new LogicalTopNToPhysicalTopN())
|
||||
.add(new LogicalAssertNumRowsToPhysicalAssertNumRows())
|
||||
.add(new LogicalOneRowRelationToPhysicalOneRowRelation())
|
||||
.add(new LogicalEmptyRelationToPhysicalEmptyRelation())
|
||||
.build();
|
||||
|
||||
public List<Rule> getExplorationRules() {
|
||||
|
||||
@ -30,6 +30,7 @@ public enum RuleType {
|
||||
|
||||
// **** make sure BINDING_UNBOUND_LOGICAL_PLAN is the lowest priority in the rewrite rules. ****
|
||||
BINDING_NON_LEAF_LOGICAL_PLAN(RuleTypeClass.REWRITE),
|
||||
BINDING_ONE_ROW_RELATION_SLOT(RuleTypeClass.REWRITE),
|
||||
BINDING_RELATION(RuleTypeClass.REWRITE),
|
||||
BINDING_PROJECT_SLOT(RuleTypeClass.REWRITE),
|
||||
BINDING_FILTER_SLOT(RuleTypeClass.REWRITE),
|
||||
@ -37,6 +38,7 @@ public enum RuleType {
|
||||
BINDING_AGGREGATE_SLOT(RuleTypeClass.REWRITE),
|
||||
BINDING_SORT_SLOT(RuleTypeClass.REWRITE),
|
||||
BINDING_LIMIT_SLOT(RuleTypeClass.REWRITE),
|
||||
BINDING_ONE_ROW_RELATION_FUNCTION(RuleTypeClass.REWRITE),
|
||||
BINDING_PROJECT_FUNCTION(RuleTypeClass.REWRITE),
|
||||
BINDING_AGGREGATE_FUNCTION(RuleTypeClass.REWRITE),
|
||||
BINDING_SUBQUERY_ALIAS_SLOT(RuleTypeClass.REWRITE),
|
||||
@ -84,6 +86,7 @@ public enum RuleType {
|
||||
COLUMN_PRUNE_SORT_CHILD(RuleTypeClass.REWRITE),
|
||||
COLUMN_PRUNE_JOIN_CHILD(RuleTypeClass.REWRITE),
|
||||
// expression of plan rewrite
|
||||
REWRITE_ONE_ROW_RELATION_EXPRESSION(RuleTypeClass.REWRITE),
|
||||
REWRITE_PROJECT_EXPRESSION(RuleTypeClass.REWRITE),
|
||||
REWRITE_AGG_EXPRESSION(RuleTypeClass.REWRITE),
|
||||
REWRITE_FILTER_EXPRESSION(RuleTypeClass.REWRITE),
|
||||
@ -96,6 +99,7 @@ public enum RuleType {
|
||||
REWRITE_SENTINEL(RuleTypeClass.REWRITE),
|
||||
OLAP_SCAN_PARTITION_PRUNE(RuleTypeClass.REWRITE),
|
||||
SWAP_FILTER_AND_PROJECT(RuleTypeClass.REWRITE),
|
||||
LOGICAL_LIMIT_TO_LOGICAL_EMPTY_RELATION_RULE(RuleTypeClass.REWRITE),
|
||||
|
||||
// exploration rules
|
||||
TEST_EXPLORATION(RuleTypeClass.EXPLORATION),
|
||||
@ -105,6 +109,7 @@ public enum RuleType {
|
||||
LOGICAL_JOIN_EXCHANGE(RuleTypeClass.EXPLORATION),
|
||||
|
||||
// implementation rules
|
||||
LOGICAL_ONE_ROW_RELATION_TO_PHYSICAL_ONE_ROW_RELATION(RuleTypeClass.IMPLEMENTATION),
|
||||
LOGICAL_AGG_TO_PHYSICAL_HASH_AGG_RULE(RuleTypeClass.IMPLEMENTATION),
|
||||
LOGICAL_JOIN_TO_HASH_JOIN_RULE(RuleTypeClass.IMPLEMENTATION),
|
||||
LOGICAL_JOIN_TO_NESTED_LOOP_JOIN_RULE(RuleTypeClass.IMPLEMENTATION),
|
||||
@ -112,6 +117,7 @@ public enum RuleType {
|
||||
LOGICAL_FILTER_TO_PHYSICAL_FILTER_RULE(RuleTypeClass.IMPLEMENTATION),
|
||||
LOGICAL_SORT_TO_PHYSICAL_QUICK_SORT_RULE(RuleTypeClass.IMPLEMENTATION),
|
||||
LOGICAL_TOP_N_TO_PHYSICAL_TOP_N_RULE(RuleTypeClass.IMPLEMENTATION),
|
||||
LOGICAL_EMPTY_RELATION_TO_PHYSICAL_EMPTY_RELATION_RULE(RuleTypeClass.IMPLEMENTATION),
|
||||
LOGICAL_LIMIT_TO_PHYSICAL_LIMIT_RULE(RuleTypeClass.IMPLEMENTATION),
|
||||
LOGICAL_OLAP_SCAN_TO_PHYSICAL_OLAP_SCAN_RULE(RuleTypeClass.IMPLEMENTATION),
|
||||
LOGICAL_ASSERT_NUM_ROWS_TO_PHYSICAL_ASSERT_NUM_ROWS(RuleTypeClass.IMPLEMENTATION),
|
||||
|
||||
@ -35,6 +35,7 @@ import org.apache.doris.nereids.trees.expressions.functions.Year;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalHaving;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
import org.apache.doris.nereids.types.DateTimeType;
|
||||
import org.apache.doris.nereids.types.DateType;
|
||||
@ -52,6 +53,22 @@ public class BindFunction implements AnalysisRuleFactory {
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
return ImmutableList.of(
|
||||
RuleType.BINDING_ONE_ROW_RELATION_FUNCTION.build(
|
||||
logicalOneRowRelation().then(oneRowRelation -> {
|
||||
List<NamedExpression> projects = oneRowRelation.getProjects();
|
||||
List<NamedExpression> boundProjects = bind(projects);
|
||||
// TODO:
|
||||
// trick logic: currently XxxRelation in GroupExpression always difference to each other,
|
||||
// so this rule must check the expression whether is changed to prevent dead loop because
|
||||
// new LogicalOneRowRelation can hit this rule too. we would remove code until the pr
|
||||
// (@wangshuo128) mark the id in XxxRelation, then we can compare XxxRelation in
|
||||
// GroupExpression by id
|
||||
if (projects.equals(boundProjects)) {
|
||||
return oneRowRelation;
|
||||
}
|
||||
return new LogicalOneRowRelation(boundProjects);
|
||||
})
|
||||
),
|
||||
RuleType.BINDING_PROJECT_FUNCTION.build(
|
||||
logicalProject().then(project -> {
|
||||
List<NamedExpression> boundExpr = bind(project.getProjects());
|
||||
|
||||
@ -19,6 +19,7 @@ package org.apache.doris.nereids.rules.analysis;
|
||||
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.analyzer.UnboundAlias;
|
||||
import org.apache.doris.nereids.analyzer.UnboundOneRowRelation;
|
||||
import org.apache.doris.nereids.analyzer.UnboundSlot;
|
||||
import org.apache.doris.nereids.analyzer.UnboundStar;
|
||||
import org.apache.doris.nereids.exceptions.AnalysisException;
|
||||
@ -44,6 +45,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalHaving;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
|
||||
@ -153,6 +155,18 @@ public class BindSlotReference implements AnalysisRuleFactory {
|
||||
return new LogicalHaving<>(boundPredicates, having.child());
|
||||
})
|
||||
),
|
||||
RuleType.BINDING_ONE_ROW_RELATION_SLOT.build(
|
||||
// we should bind UnboundAlias in the UnboundOneRowRelation
|
||||
unboundOneRowRelation().thenApply(ctx -> {
|
||||
UnboundOneRowRelation oneRowRelation = ctx.root;
|
||||
List<NamedExpression> projects = oneRowRelation.getProjects()
|
||||
.stream()
|
||||
.map(project -> bind(project, ImmutableList.of(), oneRowRelation, ctx.cascadesContext))
|
||||
.collect(Collectors.toList());
|
||||
return new LogicalOneRowRelation(projects);
|
||||
})
|
||||
),
|
||||
|
||||
RuleType.BINDING_NON_LEAF_LOGICAL_PLAN.build(
|
||||
logicalPlan()
|
||||
.when(plan -> plan.canBind() && !(plan instanceof LeafPlan))
|
||||
|
||||
@ -25,7 +25,21 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/** ProjectToGlobalAggregate. */
|
||||
/**
|
||||
* ProjectToGlobalAggregate.
|
||||
*
|
||||
* example sql:
|
||||
* <pre>
|
||||
* select sum(value)
|
||||
* from tbl
|
||||
* </pre>
|
||||
*
|
||||
* origin plan: transformed plan:
|
||||
*
|
||||
* LogicalProject(projects=[sum(value)]) LogicalAggregate(groupBy=[], output=[sum(value)])
|
||||
* | => |
|
||||
* LogicalOlapScan(table=tbl) LogicalOlapScan(table=tbl)
|
||||
*/
|
||||
public class ProjectToGlobalAggregate extends OneAnalysisRuleFactory {
|
||||
@Override
|
||||
public Rule build() {
|
||||
|
||||
@ -26,6 +26,7 @@ import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@ -42,6 +43,10 @@ import java.util.stream.Collectors;
|
||||
public class ExpressionRewrite implements RewriteRuleFactory {
|
||||
private final ExpressionRuleExecutor rewriter;
|
||||
|
||||
public ExpressionRewrite(ExpressionRewriteRule... rules) {
|
||||
this.rewriter = new ExpressionRuleExecutor(ImmutableList.copyOf(rules));
|
||||
}
|
||||
|
||||
public ExpressionRewrite(ExpressionRuleExecutor rewriter) {
|
||||
this.rewriter = Objects.requireNonNull(rewriter, "rewriter is null");
|
||||
}
|
||||
@ -49,12 +54,36 @@ public class ExpressionRewrite implements RewriteRuleFactory {
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
return ImmutableList.of(
|
||||
new OneRowRelationExpressionRewrite().build(),
|
||||
new ProjectExpressionRewrite().build(),
|
||||
new AggExpressionRewrite().build(),
|
||||
new FilterExpressionRewrite().build(),
|
||||
new JoinExpressionRewrite().build());
|
||||
}
|
||||
|
||||
private class OneRowRelationExpressionRewrite extends OneRewriteRuleFactory {
|
||||
@Override
|
||||
public Rule build() {
|
||||
return logicalOneRowRelation().then(oneRowRelation -> {
|
||||
List<NamedExpression> projects = oneRowRelation.getProjects();
|
||||
List<NamedExpression> newProjects = projects
|
||||
.stream()
|
||||
.map(expr -> (NamedExpression) rewriter.rewrite(expr))
|
||||
.collect(Collectors.toList());
|
||||
// TODO:
|
||||
// trick logic: currently XxxRelation in GroupExpression always difference to each other,
|
||||
// so this rule must check the expression whether is changed to prevent dead loop because
|
||||
// new LogicalOneRowRelation can hit this rule too. we would remove code until the pr
|
||||
// (@wangshuo128) mark the id in XxxRelation, then we can compare XxxRelation in
|
||||
// GroupExpression by id
|
||||
if (projects.equals(newProjects)) {
|
||||
return oneRowRelation;
|
||||
}
|
||||
return new LogicalOneRowRelation(newProjects);
|
||||
}).toRule(RuleType.REWRITE_ONE_ROW_RELATION_EXPRESSION);
|
||||
}
|
||||
}
|
||||
|
||||
private class ProjectExpressionRewrite extends OneRewriteRuleFactory {
|
||||
@Override
|
||||
public Rule build() {
|
||||
|
||||
@ -34,7 +34,6 @@ import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -141,21 +140,16 @@ public class TypeCoercion extends AbstractExpressionRewriteRule {
|
||||
private Expression visitImplicitCastInputTypes(Expression expr, ExpressionRewriteContext ctx) {
|
||||
ImplicitCastInputTypes implicitCastInputTypes = (ImplicitCastInputTypes) expr;
|
||||
List<Expression> newChildren = Lists.newArrayListWithCapacity(expr.arity());
|
||||
AtomicInteger changed = new AtomicInteger(0);
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < implicitCastInputTypes.expectedInputTypes().size(); i++) {
|
||||
newChildren.add(implicitCast(expr.child(i), implicitCastInputTypes.expectedInputTypes().get(i), ctx)
|
||||
.map(e -> {
|
||||
changed.incrementAndGet();
|
||||
return e;
|
||||
})
|
||||
.orElse(expr.child(0))
|
||||
);
|
||||
}
|
||||
if (changed.get() != 0) {
|
||||
return expr.withChildren(newChildren);
|
||||
} else {
|
||||
return expr;
|
||||
AbstractDataType expectedType = implicitCastInputTypes.expectedInputTypes().get(i);
|
||||
Optional<Expression> castResult = implicitCast(expr.child(i), expectedType, ctx);
|
||||
if (castResult.isPresent()) {
|
||||
changed = true;
|
||||
}
|
||||
newChildren.add(castResult.orElse(expr.child(i)));
|
||||
}
|
||||
return changed ? expr.withChildren(newChildren) : expr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.rules.implementation;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
|
||||
|
||||
/**
|
||||
* Implementation rule that convert logical empty relation to physical empty relation.
|
||||
*/
|
||||
public class LogicalEmptyRelationToPhysicalEmptyRelation extends OneImplementationRuleFactory {
|
||||
@Override
|
||||
public Rule build() {
|
||||
return logicalEmptyRelation()
|
||||
.then(relation -> new PhysicalEmptyRelation(relation.getProjects(), relation.getLogicalProperties()))
|
||||
.toRule(RuleType.LOGICAL_EMPTY_RELATION_TO_PHYSICAL_EMPTY_RELATION_RULE);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.rules.implementation;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalOneRowRelation;
|
||||
|
||||
/**
|
||||
* Implementation rule that convert logical aggregation to physical hash aggregation.
|
||||
*/
|
||||
public class LogicalOneRowRelationToPhysicalOneRowRelation extends OneImplementationRuleFactory {
|
||||
@Override
|
||||
public Rule build() {
|
||||
return logicalOneRowRelation()
|
||||
.then(relation -> new PhysicalOneRowRelation(relation.getProjects(), relation.getLogicalProperties()))
|
||||
.toRule(RuleType.LOGICAL_ONE_ROW_RELATION_TO_PHYSICAL_ONE_ROW_RELATION);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.rules.rewrite.logical;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* e.g.
|
||||
* LogicalLimit(limit=0) => LogicalEmptyRelation(projects=[limit.output()])
|
||||
*/
|
||||
public class LogicalLimitZeroToLogicalEmptyRelation extends OneRewriteRuleFactory {
|
||||
@Override
|
||||
public Rule build() {
|
||||
return logicalLimit()
|
||||
.when(limit -> limit.getLimit() == 0)
|
||||
.then(limit -> new LogicalEmptyRelation((List) limit.getOutput()))
|
||||
.toRule(RuleType.LOGICAL_LIMIT_TO_LOGICAL_EMPTY_RELATION_RULE);
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,7 @@ import org.apache.doris.catalog.MaterializedIndex;
|
||||
import org.apache.doris.catalog.OlapTable;
|
||||
import org.apache.doris.catalog.Partition;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.common.Pair;
|
||||
import org.apache.doris.nereids.memo.GroupExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
@ -28,29 +29,35 @@ 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.algebra.Aggregate;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.EmptyRelation;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.Filter;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.Limit;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.OneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.Project;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.Scan;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.TopN;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalAssertNumRows;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalTopN;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribute;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalLimit;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalLocalQuickSort;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalNestedLoopJoin;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalOneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalQuickSort;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalTopN;
|
||||
@ -96,6 +103,11 @@ public class StatsCalculator extends DefaultPlanVisitor<StatsDeriveResult, Void>
|
||||
groupExpression.setStatDerived(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsDeriveResult visitLogicalEmptyRelation(LogicalEmptyRelation emptyRelation, Void context) {
|
||||
return computeEmptyRelation(emptyRelation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsDeriveResult visitLogicalLimit(LogicalLimit<? extends Plan> limit, Void context) {
|
||||
return computeLimit(limit);
|
||||
@ -106,6 +118,11 @@ public class StatsCalculator extends DefaultPlanVisitor<StatsDeriveResult, Void>
|
||||
return computeLimit(limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsDeriveResult visitLogicalOneRowRelation(LogicalOneRowRelation oneRowRelation, Void context) {
|
||||
return computeOneRowRelation(oneRowRelation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsDeriveResult visitLogicalAggregate(LogicalAggregate<? extends Plan> aggregate, Void context) {
|
||||
return computeAggregate(aggregate);
|
||||
@ -149,11 +166,21 @@ public class StatsCalculator extends DefaultPlanVisitor<StatsDeriveResult, Void>
|
||||
return groupExpression.getCopyOfChildStats(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsDeriveResult visitPhysicalEmptyRelation(PhysicalEmptyRelation emptyRelation, Void context) {
|
||||
return computeEmptyRelation(emptyRelation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsDeriveResult visitPhysicalAggregate(PhysicalAggregate<? extends Plan> agg, Void context) {
|
||||
return computeAggregate(agg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsDeriveResult visitPhysicalOneRowRelation(PhysicalOneRowRelation oneRowRelation, Void context) {
|
||||
return computeOneRowRelation(oneRowRelation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatsDeriveResult visitPhysicalOlapScan(PhysicalOlapScan olapScan, Void context) {
|
||||
return computeScan(olapScan);
|
||||
@ -320,4 +347,34 @@ public class StatsCalculator extends DefaultPlanVisitor<StatsDeriveResult, Void>
|
||||
statsDeriveResult.setSlotToColumnStats(columnsStats);
|
||||
return statsDeriveResult;
|
||||
}
|
||||
|
||||
private StatsDeriveResult computeOneRowRelation(OneRowRelation oneRowRelation) {
|
||||
Map<Slot, ColumnStats> columnStatsMap = oneRowRelation.getProjects()
|
||||
.stream()
|
||||
.map(project -> {
|
||||
ColumnStats columnStats = new ColumnStats();
|
||||
columnStats.setNdv(1);
|
||||
// TODO: compute the literal size
|
||||
return Pair.of(project.toSlot(), columnStats);
|
||||
})
|
||||
.collect(Collectors.toMap(Pair::key, Pair::value));
|
||||
int rowCount = 1;
|
||||
return new StatsDeriveResult(rowCount, columnStatsMap);
|
||||
}
|
||||
|
||||
private StatsDeriveResult computeEmptyRelation(EmptyRelation emptyRelation) {
|
||||
Map<Slot, ColumnStats> columnStatsMap = emptyRelation.getProjects()
|
||||
.stream()
|
||||
.map(project -> {
|
||||
ColumnStats columnStats = new ColumnStats();
|
||||
columnStats.setNdv(0);
|
||||
columnStats.setMaxSize(0);
|
||||
columnStats.setNumNulls(0);
|
||||
columnStats.setAvgSize(0);
|
||||
return Pair.of(project.toSlot(), columnStats);
|
||||
})
|
||||
.collect(Collectors.toMap(Pair::key, Pair::value));
|
||||
int rowCount = 0;
|
||||
return new StatsDeriveResult(rowCount, columnStatsMap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,4 +84,20 @@ public interface TreeNode<NODE_TYPE extends TreeNode<NODE_TYPE>> {
|
||||
});
|
||||
return (T) result.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* iterate top down and test predicate if contains any instance of the classes
|
||||
* @param types classes array
|
||||
* @return true if it has any instance of the types
|
||||
*/
|
||||
default boolean containsType(Class...types) {
|
||||
return anyMatch(node -> {
|
||||
for (Class type : types) {
|
||||
if (type.isInstance(node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,17 +26,23 @@ import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Reference to slot in expression.
|
||||
*/
|
||||
public class SlotReference extends Slot {
|
||||
private final ExprId exprId;
|
||||
// TODO: we should distinguish the name is alias or column name, and the column name should contains
|
||||
// `cluster:db`.`table`.`column`
|
||||
private final String name;
|
||||
private final List<String> qualifier;
|
||||
private final DataType dataType;
|
||||
private final boolean nullable;
|
||||
|
||||
private final Column column;
|
||||
|
||||
public SlotReference(String name, DataType dataType) {
|
||||
this(NamedExpressionUtil.newExprId(), name, dataType, true, ImmutableList.of());
|
||||
}
|
||||
@ -49,6 +55,10 @@ public class SlotReference extends Slot {
|
||||
this(NamedExpressionUtil.newExprId(), name, dataType, nullable, qualifier);
|
||||
}
|
||||
|
||||
public SlotReference(ExprId exprId, String name, DataType dataType, boolean nullable, List<String> qualifier) {
|
||||
this(exprId, name, dataType, nullable, qualifier, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for SlotReference.
|
||||
*
|
||||
@ -57,18 +67,22 @@ public class SlotReference extends Slot {
|
||||
* @param dataType slot reference logical data type
|
||||
* @param nullable true if nullable
|
||||
* @param qualifier slot reference qualifier
|
||||
* @param column the column which this slot come from
|
||||
*/
|
||||
public SlotReference(ExprId exprId, String name, DataType dataType, boolean nullable, List<String> qualifier) {
|
||||
public SlotReference(ExprId exprId, String name, DataType dataType, boolean nullable,
|
||||
List<String> qualifier, @Nullable Column column) {
|
||||
this.exprId = exprId;
|
||||
this.name = name;
|
||||
this.dataType = dataType;
|
||||
this.qualifier = qualifier;
|
||||
this.nullable = nullable;
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
public static SlotReference fromColumn(Column column, List<String> qualifier) {
|
||||
DataType dataType = DataType.convertFromCatalogDataType(column.getType());
|
||||
return new SlotReference(column.getName(), dataType, column.isAllowNull(), qualifier);
|
||||
return new SlotReference(NamedExpressionUtil.newExprId(), column.getName(), dataType,
|
||||
column.isAllowNull(), qualifier, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -134,8 +148,8 @@ public class SlotReference extends Slot {
|
||||
return Objects.hash(exprId);
|
||||
}
|
||||
|
||||
public Column getColumn() {
|
||||
return new Column(name, dataType.toCatalogDataType());
|
||||
public Optional<Column> getColumn() {
|
||||
return Optional.ofNullable(column);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -158,7 +172,7 @@ public class SlotReference extends Slot {
|
||||
|
||||
@Override
|
||||
public Slot withQualifier(List<String> qualifiers) {
|
||||
return new SlotReference(exprId, name, dataType, nullable, qualifiers);
|
||||
return new SlotReference(exprId, name, dataType, nullable, qualifiers, column);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes;
|
||||
import org.apache.doris.nereids.types.DataType;
|
||||
import org.apache.doris.nereids.types.IntegerType;
|
||||
import org.apache.doris.nereids.types.StringType;
|
||||
import org.apache.doris.nereids.types.VarcharType;
|
||||
import org.apache.doris.nereids.types.coercion.AbstractDataType;
|
||||
import org.apache.doris.nereids.types.coercion.TypeCollection;
|
||||
|
||||
@ -52,9 +52,24 @@ public class Substring extends BoundFunction implements TernaryExpression, Impli
|
||||
super("substring", str, pos, new IntegerLiteral(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
public Expression getTarget() {
|
||||
return child(0);
|
||||
}
|
||||
|
||||
public Expression getPosition() {
|
||||
return child(1);
|
||||
}
|
||||
|
||||
public Expression getLength() {
|
||||
return child(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType() {
|
||||
return StringType.INSTANCE;
|
||||
if (getLength() instanceof IntegerLiteral) {
|
||||
return VarcharType.createVarcharType(((IntegerLiteral) getLength()).getValue());
|
||||
}
|
||||
return VarcharType.SYSTEM_DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
|
||||
import org.apache.doris.nereids.types.DataType;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -49,8 +50,16 @@ public abstract class Literal extends Expression implements LeafExpression {
|
||||
public static Literal of(Object value) {
|
||||
if (value == null) {
|
||||
return new NullLiteral();
|
||||
} else if (value instanceof Byte) {
|
||||
return new TinyIntLiteral(((Byte) value).byteValue());
|
||||
} else if (value instanceof Short) {
|
||||
return new SmallIntLiteral(((Short) value).shortValue());
|
||||
} else if (value instanceof Integer) {
|
||||
return new IntegerLiteral((Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
return new BigIntLiteral(((Long) value).longValue());
|
||||
} else if (value instanceof BigInteger) {
|
||||
return new LargeIntLiteral((BigInteger) value);
|
||||
} else if (value instanceof Boolean) {
|
||||
return new BooleanLiteral((Boolean) value);
|
||||
} else if (value instanceof String) {
|
||||
|
||||
@ -58,4 +58,9 @@ public class VarcharLiteral extends Literal {
|
||||
public LiteralExpr toLegacyLiteral() {
|
||||
return new StringLiteral(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "'" + value + "'";
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,9 @@ public enum PlanType {
|
||||
|
||||
// logical plan
|
||||
LOGICAL_SUBQUERY_ALIAS,
|
||||
LOGICAL_UNBOUND_ONE_ROW_RELATION,
|
||||
LOGICAL_EMPTY_RELATION,
|
||||
LOGICAL_ONE_ROW_RELATION,
|
||||
LOGICAL_UNBOUND_RELATION,
|
||||
LOGICAL_BOUND_RELATION,
|
||||
LOGICAL_PROJECT,
|
||||
@ -42,6 +45,8 @@ public enum PlanType {
|
||||
GROUP_PLAN,
|
||||
|
||||
// physical plan
|
||||
PHYSICAL_EMPTY_RELATION,
|
||||
PHYSICAL_ONE_ROW_RELATION,
|
||||
PHYSICAL_OLAP_SCAN,
|
||||
PHYSICAL_PROJECT,
|
||||
PHYSICAL_FILTER,
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.trees.plans.algebra;
|
||||
|
||||
import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Common interface for logical/physical empty relation.
|
||||
*
|
||||
* e.g.
|
||||
* select * from tbl limit 0
|
||||
*/
|
||||
public interface EmptyRelation {
|
||||
List<NamedExpression> getProjects();
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.trees.plans.algebra;
|
||||
|
||||
import org.apache.doris.nereids.analyzer.Relation;
|
||||
import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Common interface for logical/physical OneRowRelation.
|
||||
*/
|
||||
public interface OneRowRelation extends Relation {
|
||||
List<NamedExpression> getProjects();
|
||||
}
|
||||
@ -18,6 +18,7 @@
|
||||
package org.apache.doris.nereids.trees.plans.algebra;
|
||||
|
||||
import org.apache.doris.catalog.Table;
|
||||
import org.apache.doris.nereids.analyzer.Relation;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.Slot;
|
||||
|
||||
@ -27,7 +28,7 @@ import java.util.List;
|
||||
/**
|
||||
* Common interface for logical/physical scan.
|
||||
*/
|
||||
public interface Scan {
|
||||
public interface Scan extends Relation {
|
||||
List<Expression> getExpressions();
|
||||
|
||||
Table getTable();
|
||||
|
||||
@ -0,0 +1,113 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.trees.plans.logical;
|
||||
|
||||
import org.apache.doris.nereids.memo.GroupExpression;
|
||||
import org.apache.doris.nereids.properties.LogicalProperties;
|
||||
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.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.EmptyRelation;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
|
||||
import org.apache.doris.nereids.util.Utils;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A logical relation that contains empty row.
|
||||
* e.g.
|
||||
* select * from tbl limit 0
|
||||
*/
|
||||
public class LogicalEmptyRelation extends LogicalLeaf implements EmptyRelation {
|
||||
private final List<NamedExpression> projects;
|
||||
|
||||
public LogicalEmptyRelation(List<NamedExpression> projects) {
|
||||
this(projects, Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
public LogicalEmptyRelation(List<NamedExpression> projects, Optional<GroupExpression> groupExpression,
|
||||
Optional<LogicalProperties> logicalProperties) {
|
||||
super(PlanType.LOGICAL_ONE_ROW_RELATION, groupExpression, logicalProperties);
|
||||
this.projects = ImmutableList.copyOf(Objects.requireNonNull(projects, "projects can not be null"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitLogicalEmptyRelation(this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NamedExpression> getProjects() {
|
||||
return projects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Expression> getExpressions() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
|
||||
return new LogicalEmptyRelation(projects, groupExpression, Optional.of(logicalPropertiesSupplier.get()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
|
||||
return new LogicalEmptyRelation(projects, Optional.empty(), logicalProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Slot> computeOutput() {
|
||||
return projects.stream()
|
||||
.map(NamedExpression::toSlot)
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Utils.toSqlString("LogicalEmptyRelation",
|
||||
"projects", projects
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
LogicalEmptyRelation that = (LogicalEmptyRelation) o;
|
||||
return Objects.equals(projects, that.projects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(projects);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.trees.plans.logical;
|
||||
|
||||
import org.apache.doris.nereids.memo.GroupExpression;
|
||||
import org.apache.doris.nereids.properties.LogicalProperties;
|
||||
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.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.OneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
|
||||
import org.apache.doris.nereids.util.Utils;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A relation that contains only one row consist of some constant expressions.
|
||||
* e.g. select 100, 'value'
|
||||
*/
|
||||
public class LogicalOneRowRelation extends LogicalLeaf implements OneRowRelation {
|
||||
private final List<NamedExpression> projects;
|
||||
|
||||
public LogicalOneRowRelation(List<NamedExpression> projects) {
|
||||
this(projects, Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
private LogicalOneRowRelation(List<NamedExpression> projects, Optional<GroupExpression> groupExpression,
|
||||
Optional<LogicalProperties> logicalProperties) {
|
||||
super(PlanType.LOGICAL_ONE_ROW_RELATION, groupExpression, logicalProperties);
|
||||
Preconditions.checkArgument(projects.stream().noneMatch(p -> p.containsType(Slot.class)),
|
||||
"OneRowRelation can not contains any slot");
|
||||
this.projects = ImmutableList.copyOf(Objects.requireNonNull(projects, "projects can not be null"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitLogicalOneRowRelation(this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NamedExpression> getProjects() {
|
||||
return projects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Expression> getExpressions() {
|
||||
return (List) projects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
|
||||
return new LogicalOneRowRelation(projects, groupExpression, Optional.of(logicalPropertiesSupplier.get()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
|
||||
return new LogicalOneRowRelation(projects, Optional.empty(), logicalProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Slot> computeOutput() {
|
||||
return projects.stream()
|
||||
.map(NamedExpression::toSlot)
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Utils.toSqlString("LogicalOneRowRelation",
|
||||
"projects", projects
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
LogicalOneRowRelation that = (LogicalOneRowRelation) o;
|
||||
return Objects.equals(projects, that.projects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(projects);
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract class for all concrete physical plan.
|
||||
@ -54,9 +55,9 @@ public abstract class AbstractPhysicalPlan extends AbstractPlan implements Physi
|
||||
}
|
||||
|
||||
public AbstractPhysicalPlan(PlanType type, Optional<GroupExpression> groupExpression,
|
||||
LogicalProperties logicalProperties, PhysicalProperties physicalProperties, Plan... children) {
|
||||
LogicalProperties logicalProperties, @Nullable PhysicalProperties physicalProperties, Plan... children) {
|
||||
super(type, groupExpression, Optional.of(logicalProperties), children);
|
||||
this.physicalProperties = physicalProperties;
|
||||
this.physicalProperties = physicalProperties == null ? PhysicalProperties.ANY : physicalProperties;
|
||||
}
|
||||
|
||||
public PhysicalProperties getPhysicalProperties() {
|
||||
|
||||
@ -25,6 +25,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract class for all physical plan that have two children.
|
||||
@ -47,8 +48,8 @@ public abstract class PhysicalBinary<
|
||||
}
|
||||
|
||||
public PhysicalBinary(PlanType type, Optional<GroupExpression> groupExpression,
|
||||
LogicalProperties logicalProperties, PhysicalProperties physicalProperties, LEFT_CHILD_TYPE leftChild,
|
||||
RIGHT_CHILD_TYPE rightChild) {
|
||||
LogicalProperties logicalProperties, @Nullable PhysicalProperties physicalProperties,
|
||||
LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
|
||||
super(type, groupExpression, logicalProperties, physicalProperties, leftChild, rightChild);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,122 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.trees.plans.physical;
|
||||
|
||||
import org.apache.doris.nereids.memo.GroupExpression;
|
||||
import org.apache.doris.nereids.properties.LogicalProperties;
|
||||
import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
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.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.EmptyRelation;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
|
||||
import org.apache.doris.nereids.util.Utils;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A physical relation that contains empty row.
|
||||
* e.g.
|
||||
* select * from tbl limit 0
|
||||
*/
|
||||
public class PhysicalEmptyRelation extends PhysicalLeaf implements EmptyRelation {
|
||||
private final List<NamedExpression> projects;
|
||||
|
||||
public PhysicalEmptyRelation(List<NamedExpression> projects, LogicalProperties logicalProperties) {
|
||||
this(projects, Optional.empty(), logicalProperties, null);
|
||||
}
|
||||
|
||||
public PhysicalEmptyRelation(List<NamedExpression> projects, Optional<GroupExpression> groupExpression,
|
||||
LogicalProperties logicalProperties, PhysicalProperties physicalProperties) {
|
||||
super(PlanType.PHYSICAL_EMPTY_RELATION, groupExpression, logicalProperties, physicalProperties);
|
||||
this.projects = ImmutableList.copyOf(Objects.requireNonNull(projects, "projects can not be null"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitPhysicalEmptyRelation(this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Expression> getExpressions() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
|
||||
return new PhysicalEmptyRelation(projects, groupExpression,
|
||||
logicalPropertiesSupplier.get(), physicalProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
|
||||
return new PhysicalEmptyRelation(projects, Optional.empty(),
|
||||
logicalProperties.get(), physicalProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Slot> computeOutput() {
|
||||
return projects.stream()
|
||||
.map(NamedExpression::toSlot)
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Utils.toSqlString("PhysicalEmptyRelation",
|
||||
"projects", projects
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
PhysicalEmptyRelation that = (PhysicalEmptyRelation) o;
|
||||
return Objects.equals(projects, that.projects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(projects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NamedExpression> getProjects() {
|
||||
return projects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PhysicalPlan withPhysicalProperties(PhysicalProperties physicalProperties) {
|
||||
return new PhysicalEmptyRelation(projects, Optional.empty(),
|
||||
logicalPropertiesSupplier.get(), physicalProperties);
|
||||
}
|
||||
}
|
||||
@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.plans.LeafPlan;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract class for all physical plan that have no child.
|
||||
@ -40,7 +41,7 @@ public abstract class PhysicalLeaf extends AbstractPhysicalPlan implements LeafP
|
||||
}
|
||||
|
||||
public PhysicalLeaf(PlanType type, Optional<GroupExpression> groupExpression,
|
||||
LogicalProperties logicalProperties, PhysicalProperties physicalProperties) {
|
||||
LogicalProperties logicalProperties, @Nullable PhysicalProperties physicalProperties) {
|
||||
super(type, groupExpression, logicalProperties, physicalProperties);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,113 @@
|
||||
// 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.
|
||||
|
||||
package org.apache.doris.nereids.trees.plans.physical;
|
||||
|
||||
import org.apache.doris.nereids.memo.GroupExpression;
|
||||
import org.apache.doris.nereids.properties.LogicalProperties;
|
||||
import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.OneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
|
||||
import org.apache.doris.nereids.util.Utils;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A physical relation that contains only one row consist of some constant expressions.
|
||||
* e.g. select 100, 'value'
|
||||
*/
|
||||
public class PhysicalOneRowRelation extends PhysicalLeaf implements OneRowRelation {
|
||||
private final List<NamedExpression> projects;
|
||||
|
||||
public PhysicalOneRowRelation(List<NamedExpression> projects, LogicalProperties logicalProperties) {
|
||||
this(projects, Optional.empty(), logicalProperties, null);
|
||||
}
|
||||
|
||||
private PhysicalOneRowRelation(List<NamedExpression> projects, Optional<GroupExpression> groupExpression,
|
||||
LogicalProperties logicalProperties, PhysicalProperties physicalProperties) {
|
||||
super(PlanType.PHYSICAL_ONE_ROW_RELATION, groupExpression, logicalProperties, physicalProperties);
|
||||
Preconditions.checkArgument(projects.stream().allMatch(Expression::isConstant),
|
||||
"OneRowRelation must consist of some constant expression");
|
||||
this.projects = ImmutableList.copyOf(Objects.requireNonNull(projects, "projects can not be null"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitPhysicalOneRowRelation(this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NamedExpression> getProjects() {
|
||||
return projects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Expression> getExpressions() {
|
||||
return (List) projects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
|
||||
return new PhysicalOneRowRelation(projects, groupExpression,
|
||||
logicalPropertiesSupplier.get(), physicalProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
|
||||
return new PhysicalOneRowRelation(projects, Optional.empty(),
|
||||
logicalProperties.get(), physicalProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Utils.toSqlString("PhysicalOneRowRelation",
|
||||
"expressions", projects
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PhysicalOneRowRelation that = (PhysicalOneRowRelation) o;
|
||||
return Objects.equals(projects, that.projects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(projects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PhysicalOneRowRelation withPhysicalProperties(PhysicalProperties physicalProperties) {
|
||||
return new PhysicalOneRowRelation(projects, Optional.empty(),
|
||||
logicalPropertiesSupplier.get(), physicalProperties);
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,7 @@ import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
import org.apache.doris.nereids.trees.plans.UnaryPlan;
|
||||
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract class for all physical plan that have one child.
|
||||
@ -43,7 +44,7 @@ public abstract class PhysicalUnary<CHILD_TYPE extends Plan>
|
||||
}
|
||||
|
||||
public PhysicalUnary(PlanType type, Optional<GroupExpression> groupExpression,
|
||||
LogicalProperties logicalProperties, PhysicalProperties physicalProperties, CHILD_TYPE child) {
|
||||
LogicalProperties logicalProperties, @Nullable PhysicalProperties physicalProperties, CHILD_TYPE child) {
|
||||
super(type, groupExpression, logicalProperties, physicalProperties, child);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.doris.nereids.trees.plans.visitor;
|
||||
|
||||
import org.apache.doris.nereids.analyzer.UnboundOneRowRelation;
|
||||
import org.apache.doris.nereids.analyzer.UnboundRelation;
|
||||
import org.apache.doris.nereids.trees.plans.GroupPlan;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
@ -25,11 +26,13 @@ import org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalAssertNumRows;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalHaving;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalSelectHint;
|
||||
@ -40,12 +43,14 @@ import org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalSort;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribute;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalLimit;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalLocalQuickSort;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalNestedLoopJoin;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalOneRowRelation;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalQuickSort;
|
||||
import org.apache.doris.nereids.trees.plans.physical.PhysicalRelation;
|
||||
@ -81,6 +86,18 @@ public abstract class PlanVisitor<R, C> {
|
||||
return visit(alias, context);
|
||||
}
|
||||
|
||||
public R visitUnboundOneRowRelation(UnboundOneRowRelation oneRowRelation, C context) {
|
||||
return visit(oneRowRelation, context);
|
||||
}
|
||||
|
||||
public R visitLogicalEmptyRelation(LogicalEmptyRelation emptyRelation, C context) {
|
||||
return visit(emptyRelation, context);
|
||||
}
|
||||
|
||||
public R visitLogicalOneRowRelation(LogicalOneRowRelation oneRowRelation, C context) {
|
||||
return visit(oneRowRelation, context);
|
||||
}
|
||||
|
||||
public R visitUnboundRelation(UnboundRelation relation, C context) {
|
||||
return visit(relation, context);
|
||||
}
|
||||
@ -153,6 +170,14 @@ public abstract class PlanVisitor<R, C> {
|
||||
return visit(scan, context);
|
||||
}
|
||||
|
||||
public R visitPhysicalEmptyRelation(PhysicalEmptyRelation emptyRelation, C context) {
|
||||
return visit(emptyRelation, context);
|
||||
}
|
||||
|
||||
public R visitPhysicalOneRowRelation(PhysicalOneRowRelation oneRowRelation, C context) {
|
||||
return visit(oneRowRelation, context);
|
||||
}
|
||||
|
||||
public R visitPhysicalOlapScan(PhysicalOlapScan olapScan, C context) {
|
||||
return visitPhysicalScan(olapScan, context);
|
||||
}
|
||||
|
||||
@ -33,12 +33,16 @@ import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Abstract class for all data type in Nereids.
|
||||
*/
|
||||
public abstract class DataType implements AbstractDataType {
|
||||
private static final Pattern VARCHAR_PATTERN = Pattern.compile("varchar(\\(\\d+\\))?");
|
||||
|
||||
// use class and supplier here to avoid class load deadlock.
|
||||
private static final Map<Class<? extends NumericType>, Supplier<DataType>> PROMOTION_MAP
|
||||
@ -116,14 +120,21 @@ public abstract class DataType implements AbstractDataType {
|
||||
public static DataType convertFromString(String type) {
|
||||
// TODO: use a better way to resolve types
|
||||
// TODO: support varchar, char, decimal
|
||||
switch (type.toLowerCase()) {
|
||||
type = type.toLowerCase();
|
||||
switch (type) {
|
||||
case "bool":
|
||||
case "boolean":
|
||||
return BooleanType.INSTANCE;
|
||||
case "tinyint":
|
||||
return TinyIntType.INSTANCE;
|
||||
case "smallint":
|
||||
return SmallIntType.INSTANCE;
|
||||
case "int":
|
||||
return IntegerType.INSTANCE;
|
||||
case "bigint":
|
||||
return BigIntType.INSTANCE;
|
||||
case "largeint":
|
||||
return LargeIntType.INSTANCE;
|
||||
case "double":
|
||||
return DoubleType.INSTANCE;
|
||||
case "string":
|
||||
@ -133,6 +144,10 @@ public abstract class DataType implements AbstractDataType {
|
||||
case "datetime":
|
||||
return DateTimeType.INSTANCE;
|
||||
default:
|
||||
Optional<VarcharType> varcharType = matchVarchar(type);
|
||||
if (varcharType.isPresent()) {
|
||||
return varcharType.get();
|
||||
}
|
||||
throw new AnalysisException("Nereids do not support type: " + type);
|
||||
}
|
||||
}
|
||||
@ -229,4 +244,15 @@ public abstract class DataType implements AbstractDataType {
|
||||
}
|
||||
|
||||
public abstract int width();
|
||||
|
||||
private static Optional<VarcharType> matchVarchar(String type) {
|
||||
Matcher matcher = VARCHAR_PATTERN.matcher(type);
|
||||
if (matcher.find()) {
|
||||
VarcharType varcharType = matcher.groupCount() > 1
|
||||
? VarcharType.createVarcharType(Integer.valueOf(matcher.group(1)))
|
||||
: VarcharType.SYSTEM_DEFAULT;
|
||||
return Optional.of(varcharType);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -445,4 +445,18 @@ public abstract class SetOperationNode extends PlanNode {
|
||||
numInstances = Math.max(1, numInstances);
|
||||
return numInstances;
|
||||
}
|
||||
|
||||
public void finalizeForNereids(TupleDescriptor tupleDescriptor, List<SlotDescriptor> constExprSlots) {
|
||||
materializedConstExprLists.clear();
|
||||
for (List<Expr> exprList : constExprLists) {
|
||||
Preconditions.checkState(exprList.size() == constExprSlots.size());
|
||||
List<Expr> newExprList = Lists.newArrayList();
|
||||
for (int i = 0; i < exprList.size(); ++i) {
|
||||
if (constExprSlots.get(i).isMaterialized()) {
|
||||
newExprList.add(exprList.get(i));
|
||||
}
|
||||
}
|
||||
materializedConstExprLists.add(newExprList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,11 +29,11 @@ import org.apache.doris.thrift.TPlanNodeType;
|
||||
import java.util.List;
|
||||
|
||||
public class UnionNode extends SetOperationNode {
|
||||
protected UnionNode(PlanNodeId id, TupleId tupleId) {
|
||||
public UnionNode(PlanNodeId id, TupleId tupleId) {
|
||||
super(id, tupleId, "UNION", StatisticalType.UNION_NODE);
|
||||
}
|
||||
|
||||
protected UnionNode(PlanNodeId id, TupleId tupleId,
|
||||
public UnionNode(PlanNodeId id, TupleId tupleId,
|
||||
List<Expr> setOpResultExprs, boolean isInSubplan) {
|
||||
super(id, tupleId, "UNION", setOpResultExprs, isInSubplan, StatisticalType.UNION_NODE);
|
||||
}
|
||||
|
||||
@ -229,9 +229,9 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
* A -> A(B): will run into dead loop, we can not detect it in the group tree, because B usually not equals
|
||||
* to other object (e.g. UnboundRelation), but can detect the rule's invoke times.
|
||||
*
|
||||
* limit(student) limit(1) limit(1)
|
||||
* | -> | -> | -> ...
|
||||
* any any any
|
||||
* limit(1) limit(1) limit(1)
|
||||
* | -> | -> | -> ...
|
||||
* UnboundRelation(student) UnboundRelation(student) UnboundRelation(student)
|
||||
*
|
||||
* you should split A into some states:
|
||||
* 1. A(not rewrite)
|
||||
@ -310,8 +310,7 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
logicalLimit().when(limit10::equals).then(limit -> limit)
|
||||
)
|
||||
.checkGroupNum(2)
|
||||
.checkFirstRootLogicalPlan(limit10)
|
||||
.matches(
|
||||
.matchesFromRoot(
|
||||
logicalLimit(
|
||||
logicalOlapScan().when(student::equals)
|
||||
).when(limit10::equals)
|
||||
@ -335,8 +334,7 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
logicalLimit().when(limit10::equals).then(limit -> limit.withChildren(limit.child()))
|
||||
)
|
||||
.checkGroupNum(2)
|
||||
.checkFirstRootLogicalPlan(limit10)
|
||||
.matches(
|
||||
.matchesFromRoot(
|
||||
logicalLimit(
|
||||
logicalOlapScan().when(student::equals)
|
||||
).when(limit10::equals)
|
||||
@ -360,8 +358,7 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
logicalLimit().when(limit10::equals).then(limit -> limit.child())
|
||||
)
|
||||
.checkGroupNum(1)
|
||||
.checkFirstRootLogicalPlan(student)
|
||||
.matches(
|
||||
.matchesFromRoot(
|
||||
logicalOlapScan().when(student::equals)
|
||||
);
|
||||
}
|
||||
@ -383,8 +380,7 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
logicalLimit(logicalOlapScan()).when(limit10::equals).then(limit -> limit.child())
|
||||
)
|
||||
.checkGroupNum(1)
|
||||
.checkFirstRootLogicalPlan(student)
|
||||
.matches(
|
||||
.matchesFromRoot(
|
||||
logicalOlapScan().when(student::equals)
|
||||
);
|
||||
}
|
||||
@ -407,8 +403,7 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
logicalLimit(unboundRelation()).then(limit -> student)
|
||||
)
|
||||
.checkGroupNum(1)
|
||||
.checkFirstRootLogicalPlan(student)
|
||||
.matches(
|
||||
.matchesFromRoot(
|
||||
logicalOlapScan().when(student::equals)
|
||||
);
|
||||
}
|
||||
@ -433,8 +428,7 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
logicalLimit(unboundRelation()).then(limit -> limit5)
|
||||
)
|
||||
.checkGroupNum(2)
|
||||
.checkFirstRootLogicalPlan(limit5)
|
||||
.matches(
|
||||
.matchesFromRoot(
|
||||
logicalLimit(
|
||||
logicalOlapScan().when(student::equals)
|
||||
).when(limit5::equals)
|
||||
@ -460,8 +454,7 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
logicalLimit().when(limit10::equals).then(limit -> limit5)
|
||||
)
|
||||
.checkGroupNum(2)
|
||||
.checkFirstRootLogicalPlan(limit5)
|
||||
.matches(
|
||||
.matchesFromRoot(
|
||||
logicalLimit(
|
||||
logicalOlapScan().when(student::equals)
|
||||
).when(limit5::equals)
|
||||
@ -547,8 +540,7 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
logicalLimit(unboundRelation()).then(l -> limit10)
|
||||
)
|
||||
.checkGroupNum(3)
|
||||
.checkFirstRootLogicalPlan(limit10)
|
||||
.matches(
|
||||
.matchesFromRoot(
|
||||
logicalLimit(
|
||||
logicalLimit(
|
||||
logicalOlapScan().when(scan::equals)
|
||||
@ -587,8 +579,7 @@ public class MemoRewriteTest implements PatternMatchSupported {
|
||||
)
|
||||
)
|
||||
.checkGroupNum(3)
|
||||
.checkFirstRootLogicalPlan(limit5)
|
||||
.matches(
|
||||
.matchesFromRoot(
|
||||
logicalLimit(
|
||||
logicalLimit(
|
||||
unboundRelation().when(student::equals)
|
||||
|
||||
@ -20,6 +20,8 @@ package org.apache.doris.nereids.parser;
|
||||
import org.apache.doris.common.ExceptionChecker;
|
||||
import org.apache.doris.nereids.datasets.tpch.AnalyzeCheckTestBase;
|
||||
import org.apache.doris.nereids.exceptions.AnalysisException;
|
||||
import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewrite;
|
||||
import org.apache.doris.nereids.rules.expression.rewrite.rules.TypeCoercion;
|
||||
import org.apache.doris.nereids.trees.expressions.Add;
|
||||
import org.apache.doris.nereids.trees.expressions.Alias;
|
||||
import org.apache.doris.nereids.trees.expressions.And;
|
||||
@ -30,8 +32,9 @@ import org.apache.doris.nereids.trees.expressions.SlotReference;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.Count;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.Min;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.Sum;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
|
||||
import org.apache.doris.nereids.types.IntegerType;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.Literal;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
|
||||
import org.apache.doris.nereids.types.TinyIntType;
|
||||
import org.apache.doris.nereids.util.FieldChecker;
|
||||
import org.apache.doris.nereids.util.PatternMatchSupported;
|
||||
import org.apache.doris.nereids.util.PlanChecker;
|
||||
@ -50,9 +53,9 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
connectContext.setDatabase("default_cluster:test_having");
|
||||
createTables(
|
||||
"CREATE TABLE t1 (\n"
|
||||
+ " pk INT,\n"
|
||||
+ " a1 INT,\n"
|
||||
+ " a2 INT\n"
|
||||
+ " pk TINYINT,\n"
|
||||
+ " a1 TINYINT,\n"
|
||||
+ " a2 TINYINT\n"
|
||||
+ ")\n"
|
||||
+ "DUPLICATE KEY (pk)\n"
|
||||
+ "DISTRIBUTED BY HASH (pk)\n"
|
||||
@ -60,9 +63,9 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
+ " 'replication_num' = '1'\n"
|
||||
+ ");",
|
||||
"CREATE TABLE t2 (\n"
|
||||
+ " pk INT,\n"
|
||||
+ " b1 INT,\n"
|
||||
+ " b2 INT\n"
|
||||
+ " pk TINYINT,\n"
|
||||
+ " b1 TINYINT,\n"
|
||||
+ " b2 TINYINT\n"
|
||||
+ ")\n"
|
||||
+ "DUPLICATE KEY (pk)\n"
|
||||
+ "DISTRIBUTED BY HASH (pk)\n"
|
||||
@ -81,7 +84,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
public void testHavingGroupBySlot() throws Exception {
|
||||
String sql = "SELECT a1 FROM t1 GROUP BY a1 HAVING a1 > 0";
|
||||
SlotReference a1 = new SlotReference(
|
||||
new ExprId(1), "a1", IntegerType.INSTANCE, true,
|
||||
new ExprId(1), "a1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
PlanChecker.from(connectContext).analyze(sql)
|
||||
@ -90,52 +93,55 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(a1, new IntegerLiteral(0)))));
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(a1, new TinyIntLiteral((byte) 0)))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
sql = "SELECT a1 as value FROM t1 GROUP BY a1 HAVING a1 > 0";
|
||||
a1 = new SlotReference(
|
||||
new ExprId(2), "a1", IntegerType.INSTANCE, true,
|
||||
new ExprId(2), "a1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
Alias value = new Alias(new ExprId(0), a1, "value");
|
||||
PlanChecker.from(connectContext).analyze(sql)
|
||||
.applyBottomUp(new ExpressionRewrite(TypeCoercion.INSTANCE))
|
||||
.matchesFromRoot(
|
||||
logicalFilter(
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(value)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new IntegerLiteral(0)))));
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
sql = "SELECT a1 as value FROM t1 GROUP BY a1 HAVING value > 0";
|
||||
PlanChecker.from(connectContext).analyze(sql)
|
||||
.applyBottomUp(new ExpressionRewrite(TypeCoercion.INSTANCE))
|
||||
.matchesFromRoot(
|
||||
logicalFilter(
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(value)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new IntegerLiteral(0)))));
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
sql = "SELECT SUM(a2) FROM t1 GROUP BY a1 HAVING a1 > 0";
|
||||
a1 = new SlotReference(
|
||||
new ExprId(1), "a1", IntegerType.INSTANCE, true,
|
||||
new ExprId(1), "a1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
SlotReference a2 = new SlotReference(
|
||||
new ExprId(2), "a2", IntegerType.INSTANCE, true,
|
||||
new ExprId(2), "a2", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
Alias sumA2 = new Alias(new ExprId(3), new Sum(a2), "SUM(a2)");
|
||||
PlanChecker.from(connectContext).analyze(sql)
|
||||
.applyBottomUp(new ExpressionRewrite(TypeCoercion.INSTANCE))
|
||||
.matchesFromRoot(
|
||||
logicalProject(
|
||||
logicalFilter(
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(sumA2, a1)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(a1, new IntegerLiteral(0))))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(a1, new TinyIntLiteral((byte) 0))))
|
||||
).when(FieldChecker.check("projects", Lists.newArrayList(sumA2.toSlot()))));
|
||||
NamedExpressionUtil.clear();
|
||||
}
|
||||
@ -144,11 +150,11 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
public void testHavingAggregateFunction() throws Exception {
|
||||
String sql = "SELECT a1 FROM t1 GROUP BY a1 HAVING SUM(a2) > 0";
|
||||
SlotReference a1 = new SlotReference(
|
||||
new ExprId(1), "a1", IntegerType.INSTANCE, true,
|
||||
new ExprId(1), "a1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
SlotReference a2 = new SlotReference(
|
||||
new ExprId(2), "a2", IntegerType.INSTANCE, true,
|
||||
new ExprId(2), "a2", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
Alias sumA2 = new Alias(new ExprId(3), new Sum(a2), "sum(a2)");
|
||||
@ -159,7 +165,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA2)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(sumA2.toSlot(), new IntegerLiteral(0))))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(sumA2.toSlot(), new TinyIntLiteral((byte) 0))))
|
||||
).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot()))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
@ -171,16 +177,16 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA2)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(sumA2.toSlot(), new IntegerLiteral(0)))));
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(sumA2.toSlot(), new TinyIntLiteral((byte) 0)))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
sql = "SELECT a1, SUM(a2) as value FROM t1 GROUP BY a1 HAVING SUM(a2) > 0";
|
||||
a1 = new SlotReference(
|
||||
new ExprId(2), "a1", IntegerType.INSTANCE, true,
|
||||
new ExprId(2), "a1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
a2 = new SlotReference(
|
||||
new ExprId(3), "a2", IntegerType.INSTANCE, true,
|
||||
new ExprId(3), "a2", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
Alias value = new Alias(new ExprId(0), new Sum(a2), "value");
|
||||
@ -190,7 +196,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, value)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new IntegerLiteral(0)))));
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
sql = "SELECT a1, SUM(a2) as value FROM t1 GROUP BY a1 HAVING value > 0";
|
||||
@ -200,21 +206,21 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, value)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new IntegerLiteral(0)))));
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
sql = "SELECT a1, SUM(a2) FROM t1 GROUP BY a1 HAVING MIN(pk) > 0";
|
||||
a1 = new SlotReference(
|
||||
new ExprId(1), "a1", IntegerType.INSTANCE, true,
|
||||
new ExprId(1), "a1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
a2 = new SlotReference(
|
||||
new ExprId(2), "a2", IntegerType.INSTANCE, true,
|
||||
new ExprId(2), "a2", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
sumA2 = new Alias(new ExprId(3), new Sum(a2), "SUM(a2)");
|
||||
SlotReference pk = new SlotReference(
|
||||
new ExprId(0), "pk", IntegerType.INSTANCE, true,
|
||||
new ExprId(0), "pk", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
Alias minPK = new Alias(new ExprId(4), new Min(pk), "min(pk)");
|
||||
@ -225,7 +231,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA2, minPK)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(minPK.toSlot(), new IntegerLiteral(0))))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(minPK.toSlot(), new TinyIntLiteral((byte) 0))))
|
||||
).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot(), sumA2.toSlot()))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
@ -237,11 +243,11 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA1A2)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(sumA1A2.toSlot(), new IntegerLiteral(0)))));
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(sumA1A2.toSlot(), new TinyIntLiteral((byte) 0)))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
sql = "SELECT a1, SUM(a1 + a2) FROM t1 GROUP BY a1 HAVING SUM(a1 + a2 + 3) > 0";
|
||||
Alias sumA1A23 = new Alias(new ExprId(4), new Sum(new Add(new Add(a1, a2), new IntegerLiteral(3))),
|
||||
Alias sumA1A23 = new Alias(new ExprId(4), new Sum(new Add(new Add(a1, a2), new TinyIntLiteral((byte) 3))),
|
||||
"sum(((a1 + a2) + 3))");
|
||||
PlanChecker.from(connectContext).analyze(sql)
|
||||
.matchesFromRoot(
|
||||
@ -250,7 +256,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA1A2, sumA1A23)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(sumA1A23.toSlot(), new IntegerLiteral(0))))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(sumA1A23.toSlot(), new TinyIntLiteral((byte) 0))))
|
||||
).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot(), sumA1A2.toSlot()))));
|
||||
NamedExpressionUtil.clear();
|
||||
|
||||
@ -263,7 +269,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
logicalAggregate(
|
||||
logicalOlapScan()
|
||||
).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, countStar)))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(countStar.toSlot(), new IntegerLiteral(0))))
|
||||
).when(FieldChecker.check("predicates", new GreaterThan(countStar.toSlot(), new TinyIntLiteral((byte) 0))))
|
||||
).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot()))));
|
||||
NamedExpressionUtil.clear();
|
||||
}
|
||||
@ -272,15 +278,15 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
void testJoin() throws Exception {
|
||||
String sql = "SELECT a1, sum(a2) FROM t1, t2 WHERE t1.pk = t2.pk GROUP BY a1 HAVING a1 > SUM(b1)";
|
||||
SlotReference a1 = new SlotReference(
|
||||
new ExprId(1), "a1", IntegerType.INSTANCE, true,
|
||||
new ExprId(1), "a1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
SlotReference a2 = new SlotReference(
|
||||
new ExprId(2), "a2", IntegerType.INSTANCE, true,
|
||||
new ExprId(2), "a2", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
SlotReference b1 = new SlotReference(
|
||||
new ExprId(4), "b1", IntegerType.INSTANCE, true,
|
||||
new ExprId(4), "b1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t2")
|
||||
);
|
||||
Alias sumA2 = new Alias(new ExprId(6), new Sum(a2), "sum(a2)");
|
||||
@ -332,22 +338,22 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
+ "FROM t1, t2 WHERE t1.pk = t2.pk GROUP BY t1.pk, t1.pk + 1\n"
|
||||
+ "HAVING t1.pk > 0 AND COUNT(a1) + 1 > 0 AND SUM(a1 + a2) + 1 > 0 AND v1 + 1 > 0 AND v1 > 0";
|
||||
SlotReference pk = new SlotReference(
|
||||
new ExprId(1), "pk", IntegerType.INSTANCE, true,
|
||||
new ExprId(1), "pk", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
SlotReference a1 = new SlotReference(
|
||||
new ExprId(2), "a1", IntegerType.INSTANCE, true,
|
||||
new ExprId(2), "a1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
SlotReference a2 = new SlotReference(
|
||||
new ExprId(3), "a1", IntegerType.INSTANCE, true,
|
||||
new ExprId(3), "a1", TinyIntType.INSTANCE, true,
|
||||
ImmutableList.of("default_cluster:test_having", "t1")
|
||||
);
|
||||
Alias pk1 = new Alias(new ExprId(7), new Add(pk, IntegerLiteral.of(1)), "(pk + 1)");
|
||||
Alias pk11 = new Alias(new ExprId(8), new Add(new Add(pk, IntegerLiteral.of(1)), IntegerLiteral.of(1)), "((pk + 1) + 1)");
|
||||
Alias pk2 = new Alias(new ExprId(9), new Add(pk, IntegerLiteral.of(2)), "(pk + 2)");
|
||||
Alias pk1 = new Alias(new ExprId(7), new Add(pk, Literal.of((byte) 1)), "(pk + 1)");
|
||||
Alias pk11 = new Alias(new ExprId(8), new Add(new Add(pk, Literal.of((byte) 1)), Literal.of((byte) 1)), "((pk + 1) + 1)");
|
||||
Alias pk2 = new Alias(new ExprId(9), new Add(pk, Literal.of((byte) 2)), "(pk + 2)");
|
||||
Alias sumA1 = new Alias(new ExprId(10), new Sum(a1), "SUM(a1)");
|
||||
Alias countA11 = new Alias(new ExprId(11), new Add(new Count(a1), IntegerLiteral.of(1)), "(COUNT(a1) + 1)");
|
||||
Alias countA11 = new Alias(new ExprId(11), new Add(new Count(a1), Literal.of((byte) 1)), "(COUNT(a1) + 1)");
|
||||
Alias sumA1A2 = new Alias(new ExprId(12), new Sum(new Add(a1, a2)), "SUM((a1 + a2))");
|
||||
Alias v1 = new Alias(new ExprId(0), new Count(a2), "v1");
|
||||
PlanChecker.from(connectContext).analyze(sql)
|
||||
@ -368,12 +374,12 @@ public class HavingClauseTest extends AnalyzeCheckTestBase implements PatternMat
|
||||
new And(
|
||||
new And(
|
||||
new And(
|
||||
new GreaterThan(pk.toSlot(), IntegerLiteral.of(0)),
|
||||
new GreaterThan(countA11.toSlot(), IntegerLiteral.of(0))),
|
||||
new GreaterThan(new Add(sumA1A2.toSlot(), IntegerLiteral.of(1)), IntegerLiteral.of(0))),
|
||||
new GreaterThan(new Add(v1.toSlot(), IntegerLiteral.of(1)), IntegerLiteral.of(0))
|
||||
new GreaterThan(pk.toSlot(), Literal.of((byte) 0)),
|
||||
new GreaterThan(countA11.toSlot(), Literal.of((byte) 0))),
|
||||
new GreaterThan(new Add(sumA1A2.toSlot(), Literal.of((byte) 1)), Literal.of((byte) 0))),
|
||||
new GreaterThan(new Add(v1.toSlot(), Literal.of((byte) 1)), Literal.of((byte) 0))
|
||||
),
|
||||
new GreaterThan(v1.toSlot(), IntegerLiteral.of(0))
|
||||
new GreaterThan(v1.toSlot(), Literal.of((byte) 0))
|
||||
)
|
||||
))
|
||||
).when(FieldChecker.check(
|
||||
|
||||
@ -176,13 +176,13 @@ public class ExpressionRewriteTest {
|
||||
executor = new ExpressionRuleExecutor(ImmutableList.of(SimplifyCastRule.INSTANCE));
|
||||
|
||||
// deduplicate
|
||||
assertRewrite("CAST(1 AS int)", "1");
|
||||
assertRewrite("CAST('str' AS string)", "'str'");
|
||||
assertRewrite("CAST(CAST(1 AS int) AS int)", "1");
|
||||
assertRewrite("CAST(1 AS tinyint)", "1");
|
||||
assertRewrite("CAST('str' AS varchar)", "'str'");
|
||||
assertRewrite("CAST(CAST(1 AS tinyint) AS tinyint)", "1");
|
||||
|
||||
// deduplicate inside
|
||||
assertRewrite("CAST(CAST('str' AS string) AS double)", "CAST('str' AS double)");
|
||||
assertRewrite("CAST(CAST(1 AS int) AS double)", "CAST(1 AS double)");
|
||||
assertRewrite("CAST(CAST('str' AS varchar) AS double)", "CAST('str' AS double)");
|
||||
assertRewrite("CAST(CAST(1 AS tinyint) AS double)", "CAST(1 AS double)");
|
||||
}
|
||||
|
||||
private void assertRewrite(String expression, String expected) {
|
||||
|
||||
@ -28,14 +28,14 @@ public class FieldChecker {
|
||||
Field field;
|
||||
try {
|
||||
field = o.getClass().getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Check " + fieldName + " failed", e);
|
||||
}
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
Assertions.assertEquals(value, field.get(o));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Check " + fieldName + " failed", e);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -21,9 +21,6 @@ import org.apache.doris.analysis.UserIdentity;
|
||||
import org.apache.doris.catalog.Env;
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.StatementContext;
|
||||
import org.apache.doris.nereids.memo.Group;
|
||||
import org.apache.doris.nereids.memo.GroupExpression;
|
||||
import org.apache.doris.nereids.memo.Memo;
|
||||
import org.apache.doris.nereids.parser.NereidsParser;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
|
||||
@ -114,47 +111,6 @@ public class MemoTestUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static String printGroupTree(Memo memo) {
|
||||
Group root = memo.getRoot();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
printGroup(root, 0, true, builder);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static void printGroup(Group group, int depth, boolean isLastGroup, StringBuilder builder) {
|
||||
if (!group.getLogicalExpressions().isEmpty()) {
|
||||
builder.append(getIdentStr(depth + 1))
|
||||
.append(group.getPhysicalExpressions().isEmpty() ? "+--" : "|--")
|
||||
.append("logicalExpressions:\n");
|
||||
for (int i = 0; i < group.getLogicalExpressions().size(); i++) {
|
||||
GroupExpression logicalExpression = group.getLogicalExpressions().get(i);
|
||||
boolean isLastExpression = i + 1 == group.getLogicalExpressions().size();
|
||||
printGroupExpression(logicalExpression, depth + 2, isLastExpression, builder);
|
||||
}
|
||||
}
|
||||
|
||||
if (!group.getPhysicalExpressions().isEmpty()) {
|
||||
builder.append(getIdentStr(depth + 1)).append("+-- physicalExpressions:\n");
|
||||
for (int i = 0; i < group.getPhysicalExpressions().size(); i++) {
|
||||
GroupExpression logicalExpression = group.getPhysicalExpressions().get(i);
|
||||
boolean isLastExpression = i + 1 == group.getPhysicalExpressions().size();
|
||||
printGroupExpression(logicalExpression, depth + 2, isLastExpression, builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printGroupExpression(GroupExpression groupExpression, int indent,
|
||||
boolean isLastExpression, StringBuilder builder) {
|
||||
builder.append(getIdentStr(indent))
|
||||
.append(isLastExpression ? "+--" : "|--")
|
||||
.append(groupExpression.getPlan().toString()).append("\n");
|
||||
for (int i = 0; i < groupExpression.children().size(); i++) {
|
||||
Group childGroup = groupExpression.children().get(i);
|
||||
boolean isLastGroup = i + 1 == groupExpression.children().size();
|
||||
printGroup(childGroup, indent + 1, isLastGroup, builder);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getIdentStr(int indent) {
|
||||
return StringUtils.repeat(" ", indent);
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
// 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("empty_relation") {
|
||||
// enable nereids and vectorized engine
|
||||
sql "SET enable_vectorized_engine=true"
|
||||
sql "SET enable_nereids_planner=true"
|
||||
|
||||
test {
|
||||
sql "select *, substring(s_name, 1, 2) from supplier limit 0"
|
||||
result([])
|
||||
}
|
||||
|
||||
explain {
|
||||
sql "select *, substring(s_name, 1, 2) from supplier limit 0"
|
||||
contains ":VEMPTYSET"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
// 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("one_row_relation") {
|
||||
// enable nereids and vectorized engine
|
||||
sql "SET enable_vectorized_engine=true"
|
||||
sql "SET enable_nereids_planner=true"
|
||||
|
||||
test {
|
||||
sql "select 100, 'abc', substring('abc', 1, 2), substring(substring('abcdefg', 4, 3), 1, 2)"
|
||||
result([[100, "abc", "ab", "de"]])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user