[feat](nereids) support explain delete from clause #36782 (#37100)

## Proposed changes
pick from  #36782

support explain like:
explain delete from T where A=1

Issue Number: close #xxx

<!--Describe your changes.-->

(cherry picked from commit dc369cd13096dbb90700f7fbf8f35a9059d9906f)

## Proposed changes

Issue Number: close #xxx

<!--Describe your changes.-->
This commit is contained in:
minghong
2024-07-03 15:08:08 +08:00
committed by GitHub
parent 5969d6521f
commit b3f2bd20e3
8 changed files with 253 additions and 99 deletions

View File

@ -925,9 +925,12 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
if (ctx.tableAlias().strictIdentifier() != null) {
tableAlias = ctx.tableAlias().getText();
}
if (ctx.USING() == null && ctx.cte() == null && ctx.explain() == null) {
Command deleteCommand;
if (ctx.USING() == null && ctx.cte() == null) {
query = withFilter(query, Optional.ofNullable(ctx.whereClause()));
return new DeleteFromCommand(tableName, tableAlias, partitionSpec.first, partitionSpec.second, query);
deleteCommand = new DeleteFromCommand(tableName, tableAlias, partitionSpec.first,
partitionSpec.second, query);
} else {
// convert to insert into select
query = withRelations(query, ctx.relations().relation());
@ -936,8 +939,13 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
if (ctx.cte() != null) {
cte = Optional.ofNullable(withCte(query, ctx.cte()));
}
return withExplain(new DeleteFromUsingCommand(tableName, tableAlias,
partitionSpec.first, partitionSpec.second, query, cte), ctx.explain());
deleteCommand = new DeleteFromUsingCommand(tableName, tableAlias,
partitionSpec.first, partitionSpec.second, query, cte);
}
if (ctx.explain() != null) {
return withExplain(deleteCommand, ctx.explain());
} else {
return deleteCommand;
}
}

View File

@ -28,11 +28,15 @@ import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.MaterializedIndexMeta;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.common.Config;
import org.apache.doris.common.ErrorCode;
import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.nereids.NereidsPlanner;
import org.apache.doris.nereids.analyzer.UnboundAlias;
import org.apache.doris.nereids.analyzer.UnboundRelation;
import org.apache.doris.nereids.analyzer.UnboundSlot;
import org.apache.doris.nereids.analyzer.UnboundTableSinkCreator;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.glue.LogicalPlanAdapter;
import org.apache.doris.nereids.rules.RuleType;
@ -41,12 +45,17 @@ import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.InPredicate;
import org.apache.doris.nereids.trees.expressions.IsNull;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.Not;
import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
import org.apache.doris.nereids.trees.plans.Explainable;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.PlanType;
import org.apache.doris.nereids.trees.plans.commands.info.DMLCommandType;
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.physical.PhysicalDistribute;
import org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
@ -54,6 +63,7 @@ import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
import org.apache.doris.nereids.trees.plans.physical.PhysicalUnary;
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
import org.apache.doris.nereids.util.RelationUtil;
import org.apache.doris.nereids.util.Utils;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.SessionVariable;
@ -61,6 +71,7 @@ import org.apache.doris.qe.StmtExecutor;
import org.apache.doris.qe.VariableMgr;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
@ -73,13 +84,13 @@ import java.util.stream.Collectors;
/**
* delete from unique key table.
*/
public class DeleteFromCommand extends Command implements ForwardWithSync {
public class DeleteFromCommand extends Command implements ForwardWithSync, Explainable {
private final List<String> nameParts;
private final String tableAlias;
private final boolean isTempPart;
private final List<String> partitions;
private final LogicalPlan logicalQuery;
protected final List<String> nameParts;
protected final String tableAlias;
protected final boolean isTempPart;
protected final List<String> partitions;
protected final LogicalPlan logicalQuery;
/**
* constructor
@ -347,4 +358,73 @@ public class DeleteFromCommand extends Command implements ForwardWithSync {
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitDeleteFromCommand(this, context);
}
@Override
public Plan getExplainPlan(ConnectContext ctx) {
if (!ctx.getSessionVariable().isEnableNereidsDML()) {
try {
ctx.getSessionVariable().enableFallbackToOriginalPlannerOnce();
} catch (Exception e) {
throw new AnalysisException("failed to set fallback to original planner to true", e);
}
throw new AnalysisException("Nereids DML is disabled, will try to fall back to the original planner");
}
return completeQueryPlan(ctx, logicalQuery);
}
private OlapTable getTargetTable(ConnectContext ctx) {
List<String> qualifiedTableName = RelationUtil.getQualifierName(ctx, nameParts);
TableIf table = RelationUtil.getTable(qualifiedTableName, ctx.getEnv());
if (!(table instanceof OlapTable)) {
throw new AnalysisException("table must be olapTable in delete command");
}
return ((OlapTable) table);
}
/**
* for explain command
*/
public LogicalPlan completeQueryPlan(ConnectContext ctx, LogicalPlan logicalQuery) {
OlapTable targetTable = getTargetTable(ctx);
checkTargetTable(targetTable);
// add select and insert node.
List<NamedExpression> selectLists = Lists.newArrayList();
List<String> cols = Lists.newArrayList();
boolean isMow = targetTable.getEnableUniqueKeyMergeOnWrite();
String tableName = tableAlias != null ? tableAlias : targetTable.getName();
for (Column column : targetTable.getFullSchema()) {
if (column.getName().equalsIgnoreCase(Column.DELETE_SIGN)) {
selectLists.add(new UnboundAlias(new TinyIntLiteral(((byte) 1)), Column.DELETE_SIGN));
} else if (column.getName().equalsIgnoreCase(Column.SEQUENCE_COL)
&& targetTable.getSequenceMapCol() != null) {
selectLists.add(new UnboundSlot(tableName, targetTable.getSequenceMapCol()));
} else if (column.isKey()) {
selectLists.add(new UnboundSlot(tableName, column.getName()));
} else if (!isMow && (!column.isVisible() || (!column.isAllowNull() && !column.hasDefaultValue()))) {
selectLists.add(new UnboundSlot(tableName, column.getName()));
} else {
selectLists.add(new UnboundSlot(tableName, column.getName()));
}
cols.add(column.getName());
}
logicalQuery = new LogicalProject<>(selectLists, logicalQuery);
boolean isPartialUpdate = targetTable.getEnableUniqueKeyMergeOnWrite()
&& cols.size() < targetTable.getColumns().size();
logicalQuery = handleCte(logicalQuery);
// make UnboundTableSink
return UnboundTableSinkCreator.createUnboundTableSink(nameParts, cols, ImmutableList.of(),
isTempPart, partitions, isPartialUpdate, DMLCommandType.DELETE, logicalQuery);
}
protected LogicalPlan handleCte(LogicalPlan logicalPlan) {
return logicalPlan;
}
protected void checkTargetTable(OlapTable targetTable) {
if (targetTable.getKeysType() != KeysType.UNIQUE_KEYS) {
throw new AnalysisException("delete command on aggregate/duplicate table is not explainable");
}
}
}

View File

@ -17,57 +17,32 @@
package org.apache.doris.nereids.trees.plans.commands;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.nereids.analyzer.UnboundAlias;
import org.apache.doris.nereids.analyzer.UnboundSlot;
import org.apache.doris.nereids.analyzer.UnboundTableSinkCreator;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
import org.apache.doris.nereids.trees.plans.Explainable;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.PlanType;
import org.apache.doris.nereids.trees.plans.commands.info.DMLCommandType;
import org.apache.doris.nereids.trees.plans.commands.insert.InsertIntoTableCommand;
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.visitor.PlanVisitor;
import org.apache.doris.nereids.util.Utils;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.SessionVariable;
import org.apache.doris.qe.StmtExecutor;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Optional;
/**
* delete from unique key table.
*/
public class DeleteFromUsingCommand extends Command implements ForwardWithSync, Explainable {
private final List<String> nameParts;
private final String tableAlias;
private final boolean isTempPart;
private final List<String> partitions;
public class DeleteFromUsingCommand extends DeleteFromCommand {
private final Optional<LogicalPlan> cte;
private final LogicalPlan logicalQuery;
/**
* constructor
*/
public DeleteFromUsingCommand(List<String> nameParts, String tableAlias,
boolean isTempPart, List<String> partitions, LogicalPlan logicalQuery, Optional<LogicalPlan> cte) {
super(PlanType.DELETE_COMMAND);
this.nameParts = Utils.copyRequiredList(nameParts);
this.tableAlias = tableAlias;
this.isTempPart = isTempPart;
this.partitions = Utils.copyRequiredList(partitions);
super(nameParts, tableAlias, isTempPart, partitions, logicalQuery);
this.cte = cte;
this.logicalQuery = logicalQuery;
}
@Override
@ -81,63 +56,30 @@ public class DeleteFromUsingCommand extends Command implements ForwardWithSync,
executor);
}
/**
* public for test
*/
public LogicalPlan completeQueryPlan(ConnectContext ctx, LogicalPlan logicalQuery) {
OlapTable targetTable = CommandUtils.checkAndGetDeleteTargetTable(ctx, nameParts);
// add select and insert node.
List<NamedExpression> selectLists = Lists.newArrayList();
List<String> cols = Lists.newArrayList();
boolean isMow = targetTable.getEnableUniqueKeyMergeOnWrite();
String tableName = tableAlias != null ? tableAlias : targetTable.getName();
for (Column column : targetTable.getFullSchema()) {
if (column.getName().equalsIgnoreCase(Column.DELETE_SIGN)) {
selectLists.add(new UnboundAlias(new TinyIntLiteral(((byte) 1)), Column.DELETE_SIGN));
} else if (column.getName().equalsIgnoreCase(Column.SEQUENCE_COL)) {
selectLists.add(new UnboundSlot(tableName, targetTable.getSequenceMapCol()));
} else if (column.isKey()) {
selectLists.add(new UnboundSlot(tableName, column.getName()));
} else if (!isMow && (!column.isVisible() || (!column.isAllowNull() && !column.hasDefaultValue()))) {
selectLists.add(new UnboundSlot(tableName, column.getName()));
} else {
continue;
}
cols.add(column.getName());
}
logicalQuery = new LogicalProject<>(selectLists, logicalQuery);
@Override
protected LogicalPlan handleCte(LogicalPlan logicalPlan) {
if (cte.isPresent()) {
logicalQuery = ((LogicalPlan) cte.get().withChildren(logicalQuery));
logicalPlan = ((LogicalPlan) cte.get().withChildren(logicalPlan));
}
boolean isPartialUpdate = targetTable.getEnableUniqueKeyMergeOnWrite()
&& cols.size() < targetTable.getColumns().size();
// make UnboundTableSink
return UnboundTableSinkCreator.createUnboundTableSink(nameParts, cols, ImmutableList.of(),
isTempPart, partitions, isPartialUpdate, DMLCommandType.DELETE, logicalQuery);
return logicalPlan;
}
/**
* for test
*/
public LogicalPlan getLogicalQuery() {
return logicalQuery;
}
@Override
public Plan getExplainPlan(ConnectContext ctx) {
if (!ctx.getSessionVariable().isEnableNereidsDML()) {
try {
ctx.getSessionVariable().enableFallbackToOriginalPlannerOnce();
} catch (Exception e) {
throw new AnalysisException("failed to set fallback to original planner to true", e);
}
throw new AnalysisException("Nereids DML is disabled, will try to fall back to the original planner");
}
return completeQueryPlan(ctx, logicalQuery);
}
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitDeleteFromUsingCommand(this, context);
}
@Override
protected void checkTargetTable(OlapTable targetTable) {
if (targetTable.getKeysType() != KeysType.UNIQUE_KEYS) {
throw new AnalysisException("delete command on with using clause only supports unique key model");
}
}
}