[Feature](Nereids) support update unique table statement (#20313)

This commit is contained in:
mch_ucchi
2023-06-06 20:32:43 +08:00
committed by GitHub
parent 61d9bd2ba1
commit 05bdbce8fc
15 changed files with 586 additions and 47 deletions

View File

@ -159,6 +159,7 @@ DATE_ADD: 'DATE_ADD';
DATE_SUB: 'DATE_SUB';
DATE_DIFF: 'DATE_DIFF';
DBPROPERTIES: 'DBPROPERTIES';
DEFAULT: 'DEFAULT';
DEFINED: 'DEFINED';
DELETE: 'DELETE';
DELIMITED: 'DELIMITED';

View File

@ -45,6 +45,10 @@ statement
(WITH LABEL labelName=identifier)? cols=identifierList? // label and columns define
(LEFT_BRACKET hints=identifierSeq RIGHT_BRACKET)? // hint define
query #insertIntoQuery
| explain? UPDATE tableName=multipartIdentifier tableAlias
SET updateAssignmentSeq
fromClause?
whereClause #update
;
// -----------------Command accessories-----------------
@ -184,11 +188,20 @@ hintStatement
hintAssignment
: key=identifierOrText (EQ (constantValue=constant | identifierValue=identifier))?
;
updateAssignment
: col=multipartIdentifier EQ (expression | DEFAULT)
;
updateAssignmentSeq
: assignments+=updateAssignment (COMMA assignments+=updateAssignment)*
;
lateralView
: LATERAL VIEW functionName=identifier LEFT_PAREN (expression (COMMA expression)*)? RIGHT_PAREN
tableName=identifier AS columnName=identifier
;
queryOrganization
: sortClause? limitClause?
;

View File

@ -102,6 +102,9 @@ import org.apache.doris.nereids.DorisParser.TvfPropertyContext;
import org.apache.doris.nereids.DorisParser.TvfPropertyItemContext;
import org.apache.doris.nereids.DorisParser.TypeConstructorContext;
import org.apache.doris.nereids.DorisParser.UnitIdentifierContext;
import org.apache.doris.nereids.DorisParser.UpdateAssignmentContext;
import org.apache.doris.nereids.DorisParser.UpdateAssignmentSeqContext;
import org.apache.doris.nereids.DorisParser.UpdateContext;
import org.apache.doris.nereids.DorisParser.UserIdentifyContext;
import org.apache.doris.nereids.DorisParser.UserVariableContext;
import org.apache.doris.nereids.DorisParser.WhereClauseContext;
@ -210,6 +213,7 @@ import org.apache.doris.nereids.trees.plans.commands.CreatePolicyCommand;
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel;
import org.apache.doris.nereids.trees.plans.commands.InsertIntoTableCommand;
import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
import org.apache.doris.nereids.trees.plans.logical.LogicalCTE;
import org.apache.doris.nereids.trees.plans.logical.LogicalCheckPolicy;
@ -315,6 +319,23 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
return new InsertIntoTableCommand(sink, labelName);
}
@Override
public LogicalPlan visitUpdate(UpdateContext ctx) {
LogicalPlan query = withCheckPolicy(new UnboundRelation(
RelationUtil.newRelationId(), visitMultipartIdentifier(ctx.tableName)));
query = withTableAlias(query, ctx.tableAlias());
if (ctx.fromClause() != null) {
query = withRelations(query, ctx.fromClause());
}
query = withFilter(query, Optional.of(ctx.whereClause()));
String tableAlias = null;
if (ctx.tableAlias().strictIdentifier() != null) {
tableAlias = ctx.tableAlias().getText();
}
return withExplain(new UpdateCommand(visitMultipartIdentifier(ctx.tableName), tableAlias,
visitUpdateAssignmentSeq(ctx.updateAssignmentSeq()), query), ctx.explain());
}
/**
* Visit multi-statements.
*/
@ -1269,25 +1290,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
@Override
public LogicalPlan visitFromClause(FromClauseContext ctx) {
return ParserUtils.withOrigin(ctx, () -> {
LogicalPlan left = null;
for (RelationContext relation : ctx.relation()) {
// build left deep join tree
LogicalPlan right = visitRelation(relation);
left = (left == null) ? right :
new LogicalJoin<>(
JoinType.CROSS_JOIN,
ExpressionUtils.EMPTY_CONDITION,
ExpressionUtils.EMPTY_CONDITION,
JoinHint.NONE,
Optional.empty(),
left,
right);
left = withJoinRelations(left, relation);
// TODO: pivot and lateral view
}
return left;
});
return ParserUtils.withOrigin(ctx, () -> withRelations(null, ctx));
}
/* ********************************************************************************************
@ -1319,6 +1322,19 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
.collect(ImmutableList.toImmutableList());
}
@Override
public EqualTo visitUpdateAssignment(UpdateAssignmentContext ctx) {
return new EqualTo(new UnboundSlot(visitMultipartIdentifier(ctx.multipartIdentifier())),
getExpression(ctx.expression()));
}
@Override
public List<EqualTo> visitUpdateAssignmentSeq(UpdateAssignmentSeqContext ctx) {
return ctx.assignments.stream()
.map(this::visitUpdateAssignment)
.collect(Collectors.toList());
}
/**
* get OrderKey.
*
@ -1627,6 +1643,26 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
});
}
private LogicalPlan withRelations(LogicalPlan inputPlan, FromClauseContext ctx) {
LogicalPlan left = inputPlan;
for (RelationContext relation : ctx.relation()) {
// build left deep join tree
LogicalPlan right = visitRelation(relation);
left = (left == null) ? right :
new LogicalJoin<>(
JoinType.CROSS_JOIN,
ExpressionUtils.EMPTY_CONDITION,
ExpressionUtils.EMPTY_CONDITION,
JoinHint.NONE,
Optional.empty(),
left,
right);
left = withJoinRelations(left, relation);
// TODO: pivot and lateral view
}
return left;
}
private LogicalPlan withFilter(LogicalPlan input, Optional<WhereClauseContext> whereCtx) {
return input.optionalMap(whereCtx, () ->
new LogicalFilter<>(ExpressionUtils.extractConjunctionToSet(

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.
package org.apache.doris.nereids.trees.plans;
import org.apache.doris.qe.ConnectContext;
/**
* plan can be explained.
*/
public interface Explainable {
Plan getExplainPlan(ConnectContext ctx) throws Exception;
}

View File

@ -18,8 +18,10 @@
package org.apache.doris.nereids.trees.plans.commands;
import org.apache.doris.analysis.ExplainOptions;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.nereids.NereidsPlanner;
import org.apache.doris.nereids.glue.LogicalPlanAdapter;
import org.apache.doris.nereids.trees.plans.Explainable;
import org.apache.doris.nereids.trees.plans.PlanType;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
@ -65,7 +67,12 @@ public class ExplainCommand extends Command implements NoForward {
@Override
public void run(ConnectContext ctx, StmtExecutor executor) throws Exception {
LogicalPlanAdapter logicalPlanAdapter = new LogicalPlanAdapter(logicalPlan, ctx.getStatementContext());
LogicalPlan explainPlan = null;
if (!(logicalPlan instanceof Explainable)) {
throw new AnalysisException("explain a plan cannot be explained");
}
explainPlan = ((LogicalPlan) ((Explainable) logicalPlan).getExplainPlan(ctx));
LogicalPlanAdapter logicalPlanAdapter = new LogicalPlanAdapter(explainPlan, ctx.getStatementContext());
logicalPlanAdapter.setIsExplain(new ExplainOptions(level));
executor.setParsedStmt(logicalPlanAdapter);
NereidsPlanner planner = new NereidsPlanner(ctx.getStatementContext());

View File

@ -24,6 +24,8 @@ import org.apache.doris.nereids.NereidsPlanner;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.glue.LogicalPlanAdapter;
import org.apache.doris.nereids.trees.TreeNode;
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.logical.LogicalPlan;
import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapTableSink;
@ -40,8 +42,10 @@ import com.google.common.base.Strings;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
/**
* insert into select command implementation
@ -51,22 +55,22 @@ import java.util.Set;
* InsertIntoTableCommand(Query())
* ExplainCommand(Query())
*/
public class InsertIntoTableCommand extends Command implements ForwardWithSync {
public class InsertIntoTableCommand extends Command implements ForwardWithSync, Explainable {
public static final Logger LOG = LogManager.getLogger(InsertIntoTableCommand.class);
private final LogicalPlan logicalQuery;
private final String labelName;
private final @Nullable String labelName;
private NereidsPlanner planner;
private boolean isTxnBegin = false;
/**
* constructor
*/
public InsertIntoTableCommand(LogicalPlan logicalQuery, String labelName) {
public InsertIntoTableCommand(LogicalPlan logicalQuery, @Nullable String labelName) {
super(PlanType.INSERT_INTO_TABLE_COMMAND);
Preconditions.checkNotNull(logicalQuery, "logicalQuery cannot be null in InsertIntoTableCommand");
this.logicalQuery = logicalQuery;
this.logicalQuery = Objects.requireNonNull(logicalQuery,
"logicalQuery cannot be null in InsertIntoTableCommand");
this.labelName = labelName;
}
@ -150,6 +154,11 @@ public class InsertIntoTableCommand extends Command implements ForwardWithSync {
}
}
@Override
public Plan getExplainPlan(ConnectContext ctx) {
return this.logicalQuery;
}
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitInsertIntoCommand(this, context);

View File

@ -0,0 +1,156 @@
// 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.commands;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.Table;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.nereids.analyzer.UnboundOlapTableSink;
import org.apache.doris.nereids.analyzer.UnboundSlot;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.EqualTo;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
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.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.RelationUtil;
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 com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;
/**
* update command
* the two case will be handled as:
* case 1:
* update table t1 set v1 = v1 + 1 where k1 = 1 and k2 = 2;
* =>
* insert into table (v1) select v1 + 1 from table t1 where k1 = 1 and k2 = 2
* case 2:
* update t1 set t1.c1 = t2.c1, t1.c3 = t2.c3 * 100
* from t2 inner join t3 on t2.id = t3.id
* where t1.id = t2.id;
* =>
* insert into t1 (c1, c3) select t2.c1, t2.c3 * 100 from t1 join t2 inner join t3 on t2.id = t3.id where t1.id = t2.id
*/
public class UpdateCommand extends Command implements ForwardWithSync, Explainable {
private final List<EqualTo> assignments;
private final List<String> nameParts;
private final @Nullable String tableAlias;
private final LogicalPlan logicalQuery;
private OlapTable targetTable;
/**
* constructor
*/
public UpdateCommand(List<String> nameParts, @Nullable String tableAlias, List<EqualTo> assignments,
LogicalPlan logicalQuery) {
super(PlanType.UPDATE_COMMAND);
this.nameParts = ImmutableList.copyOf(Objects.requireNonNull(nameParts,
"tableName is required in update command"));
this.assignments = ImmutableList.copyOf(Objects.requireNonNull(assignments,
"assignment is required in update command"));
this.tableAlias = tableAlias;
this.logicalQuery = Objects.requireNonNull(logicalQuery, "logicalQuery is required in update command");
}
@Override
public void run(ConnectContext ctx, StmtExecutor executor) throws Exception {
new InsertIntoTableCommand(completeQueryPlan(ctx, logicalQuery), null).run(ctx, executor);
}
/**
* add LogicalOlapTableSink node, public for test.
*/
public LogicalPlan completeQueryPlan(ConnectContext ctx, LogicalPlan logicalQuery) throws AnalysisException {
checkTable(ctx);
Map<String, Expression> colNameToExpression = Maps.newHashMap();
for (EqualTo equalTo : assignments) {
List<String> nameParts = ((UnboundSlot) equalTo.left()).getNameParts();
colNameToExpression.put(nameParts.get(nameParts.size() - 1), equalTo.right());
}
List<NamedExpression> selectItems = Lists.newArrayList();
String tableName = tableAlias != null ? tableAlias : targetTable.getName();
for (Column column : targetTable.getFullSchema()) {
if (!column.isVisible()) {
continue;
}
if (colNameToExpression.containsKey(column.getName())) {
Expression expr = colNameToExpression.get(column.getName());
selectItems.add(expr instanceof UnboundSlot
? ((NamedExpression) expr)
: new Alias(expr, expr.toSql()));
} else {
selectItems.add(new UnboundSlot(tableName, column.getName()));
}
}
logicalQuery = new LogicalProject<>(selectItems, logicalQuery);
// make UnboundTableSink
return new UnboundOlapTableSink<>(nameParts, null, null, null, logicalQuery);
}
private void checkTable(ConnectContext ctx) throws AnalysisException {
if (ctx.getSessionVariable().isInDebugMode()) {
throw new AnalysisException("Update is forbidden since current session is in debug mode."
+ " Please check the following session variables: "
+ String.join(", ", SessionVariable.DEBUG_VARIABLES));
}
List<String> tableQualifier = RelationUtil.getQualifierName(ctx, nameParts);
TableIf table = RelationUtil.getTable(tableQualifier, ctx.getEnv());
if (!(table instanceof OlapTable)) {
throw new AnalysisException("target table in update command should be an olapTable");
}
targetTable = ((OlapTable) table);
if (targetTable.getType() != Table.TableType.OLAP
|| targetTable.getKeysType() != KeysType.UNIQUE_KEYS) {
throw new AnalysisException("Only unique table could be updated.");
}
}
@Override
public Plan getExplainPlan(ConnectContext ctx) throws AnalysisException {
return completeQueryPlan(ctx, logicalQuery);
}
public LogicalPlan getLogicalQuery() {
return logicalQuery;
}
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitUpdateCommand(this, context);
}
}

View File

@ -21,8 +21,10 @@ 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.plans.AbstractPlan;
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.qe.ConnectContext;
import java.util.Optional;
import java.util.function.Supplier;
@ -30,7 +32,7 @@ import java.util.function.Supplier;
/**
* Abstract class for all concrete logical plan.
*/
public abstract class AbstractLogicalPlan extends AbstractPlan implements LogicalPlan {
public abstract class AbstractLogicalPlan extends AbstractPlan implements LogicalPlan, Explainable {
private Supplier<Boolean> hasUnboundExpressions = () -> super.hasUnboundExpression();
@ -63,4 +65,9 @@ public abstract class AbstractLogicalPlan extends AbstractPlan implements Logica
return new LogicalProperties(this::computeOutput);
}
}
@Override
public Plan getExplainPlan(ConnectContext ctx) {
return this;
}
}

View File

@ -21,8 +21,10 @@ 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.plans.AbstractPlan;
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.qe.ConnectContext;
import org.apache.doris.statistics.Statistics;
import java.util.Optional;
@ -31,7 +33,7 @@ import javax.annotation.Nullable;
/**
* Abstract class for all concrete physical plan.
*/
public abstract class AbstractPhysicalPlan extends AbstractPlan implements PhysicalPlan {
public abstract class AbstractPhysicalPlan extends AbstractPlan implements PhysicalPlan, Explainable {
protected final PhysicalProperties physicalProperties;
@ -54,4 +56,9 @@ public abstract class AbstractPhysicalPlan extends AbstractPlan implements Physi
public PhysicalProperties getPhysicalProperties() {
return physicalProperties;
}
@Override
public Plan getExplainPlan(ConnectContext ctx) {
return this;
}
}

View File

@ -0,0 +1,46 @@
// 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.visitor;
import org.apache.doris.nereids.trees.plans.commands.Command;
import org.apache.doris.nereids.trees.plans.commands.CreatePolicyCommand;
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
import org.apache.doris.nereids.trees.plans.commands.InsertIntoTableCommand;
import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
/** CommandVisitor. */
public interface CommandVisitor<R, C> {
R visitCommand(Command command, C context);
default R visitExplainCommand(ExplainCommand explain, C context) {
return visitCommand(explain, context);
}
default R visitCreatePolicyCommand(CreatePolicyCommand createPolicy, C context) {
return visitCommand(createPolicy, context);
}
default R visitInsertIntoCommand(InsertIntoTableCommand insertIntoSelectCommand,
C context) {
return visitCommand(insertIntoSelectCommand, context);
}
default R visitUpdateCommand(UpdateCommand updateCommand, C context) {
return visitCommand(updateCommand, context);
}
}

View File

@ -24,9 +24,6 @@ import org.apache.doris.nereids.analyzer.UnboundTVFRelation;
import org.apache.doris.nereids.trees.plans.GroupPlan;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.commands.Command;
import org.apache.doris.nereids.trees.plans.commands.CreatePolicyCommand;
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
import org.apache.doris.nereids.trees.plans.commands.InsertIntoTableCommand;
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;
@ -103,7 +100,7 @@ import org.apache.doris.nereids.trees.plans.physical.PhysicalWindow;
* @param <R> Return type of each visit method.
* @param <C> Context type.
*/
public abstract class PlanVisitor<R, C> {
public abstract class PlanVisitor<R, C> implements CommandVisitor<R, C> {
public abstract R visit(Plan plan, C context);
@ -115,19 +112,6 @@ public abstract class PlanVisitor<R, C> {
return visit(command, context);
}
public R visitExplainCommand(ExplainCommand explain, C context) {
return visitCommand(explain, context);
}
public R visitCreatePolicyCommand(CreatePolicyCommand createPolicy, C context) {
return visitCommand(createPolicy, context);
}
public R visitInsertIntoCommand(InsertIntoTableCommand insertIntoSelectCommand,
C context) {
return visit(insertIntoSelectCommand, context);
}
// *******************************
// Logical plans
// *******************************

View File

@ -0,0 +1,126 @@
// 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;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.nereids.parser.NereidsParser;
import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.util.PlanChecker;
import org.apache.doris.nereids.util.PlanPatternMatchSupported;
import org.apache.doris.utframe.TestWithFeService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class UpdateCommandTest extends TestWithFeService implements PlanPatternMatchSupported {
@Override
public void runBeforeAll() throws Exception {
createDatabase("test");
connectContext.setDatabase("default_cluster:test");
createTable("create table t1 (\n"
+ " k1 int,\n"
+ " k2 int,\n"
+ " v1 int,\n"
+ " v2 int\n"
+ ")\n"
+ "unique key(k1, k2)\n"
+ "distributed by hash(k1) buckets 4\n"
+ "properties(\n"
+ " \"replication_num\"=\"1\"\n"
+ ")");
createTable("create table t2 (\n"
+ " k1 int,\n"
+ " k2 int,\n"
+ " v1 int,\n"
+ " v2 int\n"
+ ")\n"
+ "unique key(k1, k2)\n"
+ "distributed by hash(k1) buckets 4\n"
+ "properties(\n"
+ " \"replication_num\"=\"1\"\n"
+ ")");
createTable("create table src (\n"
+ " k1 int,\n"
+ " k2 int,\n"
+ " v1 int,\n"
+ " v2 int\n"
+ ")\n"
+ "duplicate key(k1, k2)\n"
+ "distributed by hash(k1) buckets 4\n"
+ "properties(\n"
+ " \"replication_num\"=\"1\"\n"
+ ")");
}
@Test
public void testSimpleUpdate() throws AnalysisException {
String sql = "update t1 set v1 = v1 + 2, v2 = v1 * 2 where k1 = 3";
LogicalPlan parsed = new NereidsParser().parseSingle(sql);
Assertions.assertTrue(parsed instanceof UpdateCommand);
UpdateCommand command = ((UpdateCommand) parsed);
LogicalPlan plan = command.completeQueryPlan(connectContext, command.getLogicalQuery());
PlanChecker.from(connectContext, plan)
.analyze(plan)
.rewrite()
.matches(
logicalOlapTableSink(
logicalProject(
logicalFilter(
logicalOlapScan()
)
)
)
);
}
@Test
public void testFromClauseUpdate() throws AnalysisException {
String sql = "update t1 a set v1 = t2.v1 + 2, v2 = a.v1 * 2 "
+ "from src join t2 on src.k1 = t2.k1 where t2.k1 = a.k1";
LogicalPlan parsed = new NereidsParser().parseSingle(sql);
Assertions.assertTrue(parsed instanceof UpdateCommand);
UpdateCommand command = ((UpdateCommand) parsed);
LogicalPlan plan = command.completeQueryPlan(connectContext, command.getLogicalQuery());
PlanChecker.from(connectContext, plan)
.analyze(plan)
.rewrite()
.matches(
logicalOlapTableSink(
logicalProject(
logicalJoin(
logicalJoin(
logicalProject(
logicalFilter(
logicalOlapScan()
)
),
logicalProject(
logicalOlapScan())
),
logicalProject(
logicalFilter(
logicalOlapScan()
)
)
)
)
)
);
}
}

View File

@ -2,15 +2,15 @@
-- !sql --
1 10 1 1 1.0 2000-01-01
2 20 2 2 2.0 2000-01-02
3 30 3 3 3.0 2000-01-03
3 30 5 3 3.0 2000-01-03
-- !sql --
1 10 2 1 2.0 2000-01-01
2 20 2 2 2.0 2000-01-02
3 30 3 3 3.0 2000-01-03
3 30 5 3 3.0 2000-01-03
-- !sql --
1 10 10 1 1000.0 2000-01-01
2 20 2 2 2.0 2000-01-02
3 30 3 3 3.0 2000-01-03
3 30 5 3 3.0 2000-01-03

View File

@ -0,0 +1,81 @@
// 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("load") {
sql '''
create table t1 (
id int,
id1 int,
c1 bigint,
c2 string,
c3 double,
c4 date
) unique key (id, id1)
distributed by hash(id, id1)
properties(
'replication_num'='1',
"function_column.sequence_col" = "c4"
);
'''
sql '''
create table t2 (
id int,
c1 bigint,
c2 string,
c3 double,
c4 date
) unique key (id)
distributed by hash(id)
properties(
'replication_num'='1'
);
'''
sql '''
create table t3 (
id int
) distributed by hash(id)
properties(
'replication_num'='1'
);
'''
sql '''
INSERT INTO t1 VALUES
(1, 10, 1, '1', 1.0, '2000-01-01'),
(2, 20, 2, '2', 2.0, '2000-01-02'),
(3, 30, 3, '3', 3.0, '2000-01-03');
'''
sql '''
INSERT INTO t2 VALUES
(1, 10, '10', 10.0, '2000-01-10'),
(2, 20, '20', 20.0, '2000-01-20'),
(3, 30, '30', 30.0, '2000-01-30'),
(4, 4, '4', 4.0, '2000-01-04'),
(5, 5, '5', 5.0, '2000-01-05');
'''
sql '''
INSERT INTO t3 VALUES
(1),
(4),
(5);
'''
}

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.
suite('update_unique_table') {
sql 'set enable_nereids_planner=true'
sql 'set enable_fallback_to_original_planner=false'
sql 'set enable_nereids_dml=true'
sql 'update t1 set c1 = 5 where id = 3'
qt_sql 'select * from t1 order by id'
sql 'update t1 set c1 = c1 + 1, c3 = c2 * 2 where id = 1'
qt_sql 'select * from t1 order by id'
sql '''
update t1
set t1.c1 = t2.c1, t1.c3 = t2.c3 * 100
from t2 inner join t3 on t2.id = t3.id
where t1.id = t2.id;
'''
qt_sql 'select * from t1 order by id'
}