[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:
924060929
2022-09-08 12:21:13 +08:00
committed by GitHub
parent 2a64571bef
commit 74ffdbeebc
50 changed files with 1321 additions and 179 deletions

View File

@ -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().
*/

View File

@ -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);

View File

@ -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 {
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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),

View File

@ -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() {

View File

@ -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),

View File

@ -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());

View File

@ -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))

View File

@ -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() {

View File

@ -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() {

View File

@ -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;
}
/**

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
});
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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) {

View File

@ -58,4 +58,9 @@ public class VarcharLiteral extends Literal {
public LiteralExpr toLegacyLiteral() {
return new StringLiteral(value);
}
@Override
public String toString() {
return "'" + value + "'";
}
}

View File

@ -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,

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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(

View File

@ -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) {

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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"
}
}

View File

@ -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"]])
}
}