This is an automated email from the ASF dual-hosted git repository. morrysnow pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new 699159698e [enhancement](planner) support update from syntax (#17639) 699159698e is described below commit 699159698efe13920a183cfbcc563649ea543cbb Author: morrySnow <101034200+morrys...@users.noreply.github.com> AuthorDate: Tue Mar 14 19:26:30 2023 +0800 [enhancement](planner) support update from syntax (#17639) support update from syntax note: enable_concurrent_update is not supported now ``` UPDATE <target_table> SET <col_name> = <value> [ , <col_name> = <value> , ... ] [ FROM <additional_tables> ] [ WHERE <condition> ] ``` for example: t1 ``` +----+----+----+-----+------------+ | id | c1 | c2 | c3 | c4 | +----+----+----+-----+------------+ | 3 | 3 | 3 | 3.0 | 2000-01-03 | | 2 | 2 | 2 | 2.0 | 2000-01-02 | | 1 | 1 | 1 | 1.0 | 2000-01-01 | +----+----+----+-----+------------+ ``` t2 ``` +----+----+----+------+------------+ | id | c1 | c2 | c3 | c4 | +----+----+----+------+------------+ | 4 | 4 | 4 | 4.0 | 2000-01-04 | | 2 | 20 | 20 | 20.0 | 2000-01-20 | | 5 | 5 | 5 | 5.0 | 2000-01-05 | | 1 | 10 | 10 | 10.0 | 2000-01-10 | | 3 | 30 | 30 | 30.0 | 2000-01-30 | +----+----+----+------+------------+ ``` t3 ``` +----+ | id | +----+ | 1 | | 5 | | 4 | +----+ ``` do update ```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; ``` the result ``` +----+----+----+--------+------------+ | id | c1 | c2 | c3 | c4 | +----+----+----+--------+------------+ | 3 | 3 | 3 | 3.0 | 2000-01-03 | | 2 | 2 | 2 | 2.0 | 2000-01-02 | | 1 | 10 | 1 | 1000.0 | 2000-01-01 | +----+----+----+--------+------------+ ``` --- .../Manipulation/UPDATE.md | 125 +++++++++-- .../Manipulation/UPDATE.md | 123 +++++++++-- fe/fe-core/src/main/cup/sql_parser.cup | 47 +++- .../java/org/apache/doris/analysis/UpdateStmt.java | 174 +++++++-------- .../main/java/org/apache/doris/catalog/Env.java | 7 - .../apache/doris/load/update/UpdateManager.java | 89 -------- .../apache/doris/load/update/UpdatePlanner.java | 192 ----------------- .../doris/load/update/UpdateStmtExecutor.java | 238 --------------------- .../main/java/org/apache/doris/qe/DdlExecutor.java | 3 - .../java/org/apache/doris/qe/SessionVariable.java | 13 ++ .../java/org/apache/doris/qe/StmtExecutor.java | 16 ++ .../doris/load/update/UpdateManagerTest.java | 66 ------ .../doris/load/update/UpdateStmtExecutorTest.java | 101 --------- .../java/org/apache/doris/planner/PlannerTest.java | 8 - .../apache/doris/planner/UpdatePlannerTest.java | 185 ---------------- regression-test/data/update/test_update_unique.out | 5 + .../suites/update/test_update_unique.groovy | 64 ++++-- 17 files changed, 436 insertions(+), 1020 deletions(-) diff --git a/docs/en/docs/sql-manual/sql-reference/Data-Manipulation-Statements/Manipulation/UPDATE.md b/docs/en/docs/sql-manual/sql-reference/Data-Manipulation-Statements/Manipulation/UPDATE.md index 5605f62d13..1ec248c653 100644 --- a/docs/en/docs/sql-manual/sql-reference/Data-Manipulation-Statements/Manipulation/UPDATE.md +++ b/docs/en/docs/sql-manual/sql-reference/Data-Manipulation-Statements/Manipulation/UPDATE.md @@ -32,33 +32,63 @@ UPDATE ### Description -This statement is used to update the data (the update statement currently only supports the Unique Key model). +This statement is used to update the data. The UPDATE statement currently only supports the UNIQUE KEY model. + +#### Syntax ```sql -UPDATE table_name +UPDATE target_table SET assignment_list - WHERE expression + WHERE condition -value: - {expr | DEFAULT} +assignment_list: + assignment [, assignment] ... assignment: col_name = value -assignment_list: - assignment [, assignment] ... -```` +value: + {expr | DEFAULT} +``` + +<version since="dev"> + +UPDATE_FROM + +</version> + +```sql +UPDATE target_table + SET assignment_list + [ FROM additional_tables] + WHERE condition +``` - Parameters +#### Required Parameters -+ table_name: The target table of the data to be updated. Can be of the form 'db_name.table_name' ++ target_table: The target table of the data to be updated. Can be of the form 'db_name.table_name' + assignment_list: The target column to be updated, in the format 'col_name = value, col_name = value' -+ where expression: the condition that is expected to be updated, an expression that returns true or false can be ++ where condition: the condition that is expected to be updated, an expression that returns true or false can be - Note +#### Optional Parameters -The current UPDATE statement only supports row updates on the Unique model, and there may be data conflicts caused by concurrent updates. -At present, Doris does not deal with such problems, and users need to avoid such problems from the business side. +<version since="dev"> + +UPDATE_FROM + +</version> + ++ FROM additional_tables: Specifies one or more tables to use for selecting rows to update or for setting new values. Note that if you want use target table here, you should give it a alias explicitly. + +#### Note + +<version since="dev"> + +UPDATE_FROM + +</version> + +The current UPDATE statement only supports row updates on the Unique model. ### Example @@ -68,13 +98,76 @@ The `test` table is a unique model table, which contains four columns: k1, k2, v ```sql UPDATE test SET v1 = 1 WHERE k1=1 and k2=2; -```` +``` 2. Increment the v1 column of the k1=1 column in the 'test' table by 1 ```sql UPDATE test SET v1 = v1+1 WHERE k1=1; -```` +``` + +<version since="dev"> + +UPDATE_FROM + +</version> + +3. use the result of `t2` join `t3` to update `t1` + +```sql +-- create t1, t2, t3 tables +CREATE TABLE t1 + (id INT, c1 BIGINT, c2 STRING, c3 DOUBLE, c4 DATE) +UNIQUE KEY (id) +DISTRIBUTED BY HASH (id) +PROPERTIES('replication_num'='1', "function_column.sequence_col" = "c4"); + +CREATE TABLE t2 + (id INT, c1 BIGINT, c2 STRING, c3 DOUBLE, c4 DATE) +DISTRIBUTED BY HASH (id) +PROPERTIES('replication_num'='1'); + +CREATE TABLE t3 + (id INT) +DISTRIBUTED BY HASH (id) +PROPERTIES('replication_num'='1'); + +-- insert data +INSERT INTO t1 VALUES + (1, 1, '1', 1.0, '2000-01-01'), + (2, 2, '2', 2.0, '2000-01-02'), + (3, 3, '3', 3.0, '2000-01-03'); + +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'); + +INSERT INTO t3 VALUES + (1), + (4), + (5); + +-- update t1 +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; +``` + +the expect result is only update the row where id = 1 in table t1 + +``` ++----+----+----+--------+------------+ +| id | c1 | c2 | c3 | c4 | ++----+----+----+--------+------------+ +| 1 | 10 | 1 | 1000.0 | 2000-01-01 | +| 2 | 2 | 2 | 2.0 | 2000-01-02 | +| 3 | 3 | 3 | 3.0 | 2000-01-03 | ++----+----+----+--------+------------+ +``` ### Keywords diff --git a/docs/zh-CN/docs/sql-manual/sql-reference/Data-Manipulation-Statements/Manipulation/UPDATE.md b/docs/zh-CN/docs/sql-manual/sql-reference/Data-Manipulation-Statements/Manipulation/UPDATE.md index 21160d424d..72164fba71 100644 --- a/docs/zh-CN/docs/sql-manual/sql-reference/Data-Manipulation-Statements/Manipulation/UPDATE.md +++ b/docs/zh-CN/docs/sql-manual/sql-reference/Data-Manipulation-Statements/Manipulation/UPDATE.md @@ -32,33 +32,64 @@ UPDATE ### Description -该语句是为进行对数据进行更新的操作,( update 语句目前仅支持 Unique Key 模型)。 +该语句是为进行对数据进行更新的操作,UPDATE 语句目前仅支持 UNIQUE KEY 模型。 + +#### Syntax ```sql -UPDATE table_name +UPDATE target_table SET assignment_list - WHERE expression + WHERE condition -value: - {expr | DEFAULT} +assignment_list: + assignment [, assignment] ... assignment: col_name = value -assignment_list: - assignment [, assignment] ... +value: + {expr | DEFAULT} ``` - Parameters +<version since="dev"> + +UPDATE_FROM -+ table_name: 待更新数据的目标表。可以是 'db_name.table_name' 形式 +</version> + +```sql +UPDATE target_table + SET assignment_list + [ FROM additional_tables] + WHERE condition +``` + + +#### Required Parameters + ++ target_table: 待更新数据的目标表。可以是 'db_name.table_name' 形式 + assignment_list: 待更新的目标列,形如 'col_name = value, col_name = value' 格式 -+ where expression: 期望更新的条件,一个返回 true 或者 false 的表达式即可 ++ WHERE condition: 期望更新的条件,一个返回 true 或者 false 的表达式即可 + +#### Optional Parameters + +<version since="dev"> - Note +UPDATE_FROM -当前 UPDATE 语句仅支持在 Unique 模型上的行更新,存在并发更新导致的数据冲突可能。 -目前 Doris 并不处理这类问题,需要用户从业务侧规避这类问题。 +</version> + ++ FROM additional_tables: 指定一个或多个表,用于选中更新的行,或者获取更新的值。注意,如需要在此列表中再次使用目标表,需要为其显式指定别名。 + +#### Note + +<version since="dev"> + +UPDATE_FROM + +</version> + +当前 UPDATE 语句仅支持在 Unique 模型上的行更新。 ### Example @@ -76,9 +107,69 @@ UPDATE test SET v1 = 1 WHERE k1=1 and k2=2; UPDATE test SET v1 = v1+1 WHERE k1=1; ``` -### Keywords +<version since="dev"> - UPDATE +UPDATE_FROM -### Best Practice +</version> +3. 使用`t2`和`t3`表连接的结果,更新`t1` + +```sql +-- 创建t1, t2, t3三张表 +CREATE TABLE t1 + (id INT, c1 BIGINT, c2 STRING, c3 DOUBLE, c4 DATE) +UNIQUE KEY (id) +DISTRIBUTED BY HASH (id) +PROPERTIES('replication_num'='1', "function_column.sequence_col" = "c4"); + +CREATE TABLE t2 + (id INT, c1 BIGINT, c2 STRING, c3 DOUBLE, c4 DATE) +DISTRIBUTED BY HASH (id) +PROPERTIES('replication_num'='1'); + +CREATE TABLE t3 + (id INT) +DISTRIBUTED BY HASH (id) +PROPERTIES('replication_num'='1'); + +-- 插入数据 +INSERT INTO t1 VALUES + (1, 1, '1', 1.0, '2000-01-01', '2000-01-01'), + (2, 2, '2', 2.0, '2000-01-02', '2000-01-02'), + (3, 3, '3', 3.0, '2000-01-03', '2000-01-03'); + +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'); + +INSERT INTO t3 VALUES + (1), + (4), + (5); + +-- 更新 t1 +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; +``` + +预期结果为,更新了`t1`表`id`为`1`的列 + +``` ++----+----+----+--------+------------+ +| id | c1 | c2 | c3 | c4 | ++----+----+----+--------+------------+ +| 1 | 10 | 1 | 1000.0 | 2000-01-01 | +| 2 | 2 | 2 | 2.0 | 2000-01-02 | +| 3 | 3 | 3 | 3.0 | 2000-01-03 | ++----+----+----+--------+------------+ +``` + +### Keywords + + UPDATE diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup index ca10463d88..fb05c5af66 100644 --- a/fe/fe-core/src/main/cup/sql_parser.cup +++ b/fe/fe-core/src/main/cup/sql_parser.cup @@ -685,6 +685,9 @@ nonterminal InsertStmt insert_stmt; nonterminal InsertTarget insert_target; nonterminal InsertSource insert_source; nonterminal UpdateStmt update_stmt; +nonterminal List<BinaryPredicate> set_clause; +nonterminal List<BinaryPredicate> assignment_list; +nonterminal BinaryPredicate assignment; nonterminal BackupStmt backup_stmt; nonterminal AbstractBackupTableRefClause opt_backup_table_ref_list; @@ -751,6 +754,7 @@ nonterminal FunctionCallExpr column_slice; nonterminal ArrayList<TableRef> table_ref_list, base_table_ref_list; nonterminal ArrayList<LateralViewRef> opt_lateral_view_ref_list, lateral_view_ref_list; nonterminal FromClause from_clause; +nonterminal FromClause opt_from_clause; nonterminal TableRef table_ref; nonterminal TableRef base_table_ref; nonterminal LateralViewRef lateral_view_ref; @@ -4445,9 +4449,48 @@ insert_source ::= // update stmt update_stmt ::= - KW_UPDATE table_name:tbl KW_SET expr_list:setExprs where_clause:whereClause + KW_UPDATE table_name:tbl set_clause:setClause opt_from_clause:fromClause where_clause:whereClause {: - RESULT = new UpdateStmt(tbl, setExprs, whereClause); + RESULT = new UpdateStmt(tbl, setClause, fromClause, whereClause); + :} + ; + +opt_from_clause ::= + /* empty */ + {: + RESULT = null; + :} + | from_clause:fromClause + {: + RESULT = fromClause; + :} + ; + +set_clause ::= + KW_SET assignment_list:list + {: + RESULT = list; + :} + ; + +assignment_list ::= + assignment:a + {: + List<BinaryPredicate> list = new ArrayList<>(); + list.add(a); + RESULT = list; + :} + | assignment_list:list COMMA assignment:a + {: + list.add(a); + RESULT = list; + :} + ; + +assignment ::= + column_ref:columnRef EQUAL expr:value + {: + RESULT = new BinaryPredicate(BinaryPredicate.Operator.EQ, columnRef, value); :} ; diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/UpdateStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/UpdateStmt.java index 979fd06730..7251a7e76d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/UpdateStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/UpdateStmt.java @@ -17,12 +17,12 @@ package org.apache.doris.analysis; +import org.apache.doris.catalog.Column; import org.apache.doris.catalog.Database; import org.apache.doris.catalog.Env; import org.apache.doris.catalog.KeysType; import org.apache.doris.catalog.OlapTable; import org.apache.doris.catalog.Table; -import org.apache.doris.catalog.Type; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; @@ -30,88 +30,116 @@ import org.apache.doris.common.UserException; import org.apache.doris.common.util.Util; import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.qe.ConnectContext; -import org.apache.doris.rewrite.ExprRewriter; +import org.apache.doris.qe.SessionVariable; import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; import java.util.List; import java.util.Set; import java.util.TreeSet; /** - * UPDATE is a DML statement that modifies rows in a table. + * UPDATE is a DML statement that modifies rows in a unique key olap table. * The current update syntax only supports updating the filtered data of a single table. - * + * <p> * UPDATE table_reference * SET assignment_list + * [from_clause] * [WHERE where_condition] - * - * value: - * {expr} - * - * assignment: - * col_name = value - * + * <p> * assignment_list: * assignment [, assignment] ... + * <p> + * assignment: + * col_name = value + * <p> + * value: + * {expr} */ public class UpdateStmt extends DdlStmt { - private TableName tableName; - private List<Expr> setExprs; - private Expr whereExpr; - - // After analyzed + private final TableName tableName; + private final List<BinaryPredicate> setExprs; + private final Expr whereExpr; + private final FromClause fromClause; + private InsertStmt insertStmt; private Table targetTable; - private TupleDescriptor srcTupleDesc; + List<SelectListItem> selectListItems = Lists.newArrayList(); + List<String> cols = Lists.newArrayList(); - public UpdateStmt(TableName tableName, List<Expr> setExprs, Expr whereExpr) { + public UpdateStmt(TableName tableName, List<BinaryPredicate> setExprs, FromClause fromClause, Expr whereExpr) { this.tableName = tableName; this.setExprs = setExprs; + this.fromClause = fromClause; this.whereExpr = whereExpr; - } - - public TableName getTableName() { - return tableName; - } - - public List<Expr> getSetExprs() { - return setExprs; - } - - public Expr getWhereExpr() { - return whereExpr; - } - public Table getTargetTable() { - return targetTable; } - public TupleDescriptor getSrcTupleDesc() { - return srcTupleDesc; + public InsertStmt getInsertStmt() { + return insertStmt; } @Override public void analyze(Analyzer analyzer) throws UserException { super.analyze(analyzer); + if (ConnectContext.get() != null && ConnectContext.get().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)); + } analyzeTargetTable(analyzer); analyzeSetExprs(analyzer); - analyzeWhereExpr(analyzer); + constructInsertStmt(); + } + + private void constructInsertStmt() { + // not use origin from clause, because we need to mod it, and this action will affect toSql(). + FromClause fromUsedInInsert; + TableRef tableRef = new TableRef(tableName, null); + if (fromClause == null) { + fromUsedInInsert = new FromClause(Lists.newArrayList(tableRef)); + } else { + fromUsedInInsert = fromClause.clone(); + fromUsedInInsert.getTableRefs().add(0, tableRef); + } + SelectStmt selectStmt = new SelectStmt( + // select list + new SelectList(selectListItems, false), + // from clause + fromUsedInInsert, + // where expr + whereExpr, + // group by + null, + // having + null, + // order by + null, + // limit + LimitElement.NO_LIMIT + ); + + insertStmt = new InsertStmt( + new InsertTarget(tableName, null), + null, + cols, + new InsertSource(selectStmt), + null); } private void analyzeTargetTable(Analyzer analyzer) throws AnalysisException { - // step1: analyze table name + // step1: analyze table name and origin table alias tableName.analyze(analyzer); // disallow external catalog Util.prohibitExternalCatalog(tableName.getCtl(), this.getClass().getSimpleName()); - - // check priv + // check load privilege, select privilege will check when analyze insert stmt if (!Env.getCurrentEnv().getAccessManager() .checkTblPriv(ConnectContext.get(), tableName.getDb(), tableName.getTbl(), PrivPredicate.LOAD)) { ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "LOAD"); } - // step2: resolve table name with catalog, only unique olap table could be update + // step2: resolve table name with catalog, only unique olap table could be updated String dbName = tableName.getDb(); String targetTableName = tableName.getTbl(); Preconditions.checkNotNull(dbName); @@ -120,12 +148,12 @@ public class UpdateStmt extends DdlStmt { targetTable = database.getTableOrAnalysisException(tableName.getTbl()); if (targetTable.getType() != Table.TableType.OLAP || ((OlapTable) targetTable).getKeysType() != KeysType.UNIQUE_KEYS) { - throw new AnalysisException("Only unique olap table could be updated."); + throw new AnalysisException("Only unique table could be updated."); } - // step3: register tuple desc + // register table to ensure we could analyze column name on the left side of set exprs. targetTable.readLock(); try { - srcTupleDesc = analyzer.registerOlapTable(targetTable, tableName, null); + analyzer.registerOlapTable(targetTable, tableName, null); } finally { targetTable.readUnlock(); } @@ -134,14 +162,9 @@ public class UpdateStmt extends DdlStmt { private void analyzeSetExprs(Analyzer analyzer) throws AnalysisException { // step1: analyze set exprs Set<String> columnMappingNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - // the column expr only support binary predicate which's child(0) must be a SloRef. + // the column expr only support binary predicate which child(0) must be a SloRef. // the duplicate column name of SloRef is forbidden. - for (Expr setExpr : setExprs) { - if (!(setExpr instanceof BinaryPredicate)) { - throw new AnalysisException("Set function expr only support eq binary predicate. " - + "Expr: " + setExpr.toSql()); - } - BinaryPredicate predicate = (BinaryPredicate) setExpr; + for (BinaryPredicate predicate : setExprs) { if (predicate.getOp() != BinaryPredicate.Operator.EQ) { throw new AnalysisException("Set function expr only support eq binary predicate. " + "The predicate operator error, op: " + predicate.getOp()); @@ -149,7 +172,7 @@ public class UpdateStmt extends DdlStmt { Expr lhs = predicate.getChild(0); if (!(lhs instanceof SlotRef)) { throw new AnalysisException("Set function expr only support eq binary predicate " - + "which's child(0) must be a column name. " + + "which child(0) must be a column name. " + "The child(0) expr error. expr: " + lhs.toSql()); } String column = ((SlotRef) lhs).getColumnName(); @@ -159,8 +182,7 @@ public class UpdateStmt extends DdlStmt { } // step2: resolve target columns with catalog, // only value columns which belong to target table could be updated. - for (Expr setExpr : setExprs) { - Preconditions.checkState(setExpr instanceof BinaryPredicate); + for (BinaryPredicate setExpr : setExprs) { // check target column // 1. columns must belong to target table // 2. only value columns could be updated @@ -170,41 +192,22 @@ public class UpdateStmt extends DdlStmt { } lhs.analyze(analyzer); if (((SlotRef) lhs).getColumn().isKey()) { - throw new AnalysisException("Only value columns of unique table could be updated."); + throw new AnalysisException("Only value columns of unique table could be updated"); } - // check set expr of target column - Expr rhs = setExpr.getChild(1); - checkLargeIntOverflow(rhs); - rhs.analyze(analyzer); - if (lhs.getType() != rhs.getType()) { - setExpr.setChild(1, rhs.checkTypeCompatibility(lhs.getType())); - } - } - } - - /* - The overflow detection of LargeInt needs to be verified again here. - The reason is: the first overflow detection(in constructor) cannot filter 2^127. - Therefore, a second verification is required here. - */ - private void checkLargeIntOverflow(Expr expr) throws AnalysisException { - if (expr instanceof LargeIntLiteral) { - expr.analyzeImpl(analyzer); } - } - private void analyzeWhereExpr(Analyzer analyzer) throws AnalysisException { - if (whereExpr == null) { - throw new AnalysisException("Where clause is required"); - } - whereExpr.analyze(analyzer); - whereExpr = analyzer.getExprRewriter().rewrite(whereExpr, analyzer, ExprRewriter.ClauseType.WHERE_CLAUSE); - whereExpr.reset(); - whereExpr.analyze(analyzer); - if (!whereExpr.getType().equals(Type.BOOLEAN)) { - throw new AnalysisException("Where clause is not a valid statement return bool"); + // step3: generate select list and insert column name list in insert stmt + for (Column column : targetTable.getColumns()) { + Expr expr = new SlotRef(tableName, column.getName()); + for (BinaryPredicate setExpr : setExprs) { + Expr lhs = setExpr.getChild(0); + if (((SlotRef) lhs).getColumn().equals(column)) { + expr = setExpr.getChild(1); + } + } + selectListItems.add(new SelectListItem(expr, null)); + cols.add(column.getName()); } - analyzer.registerConjunct(whereExpr, srcTupleDesc.getId()); } @Override @@ -215,6 +218,9 @@ public class UpdateStmt extends DdlStmt { for (Expr setExpr : setExprs) { sb.append(setExpr.toSql()).append(", "); } + if (fromClause != null) { + sb.append("\n").append(fromClause.toSql()); + } sb.append("\n"); if (whereExpr != null) { sb.append(" ").append("WHERE ").append(whereExpr.toSql()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java index 43b5a26d52..5190f8106d 100755 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java @@ -162,7 +162,6 @@ import org.apache.doris.load.routineload.RoutineLoadScheduler; import org.apache.doris.load.routineload.RoutineLoadTaskScheduler; import org.apache.doris.load.sync.SyncChecker; import org.apache.doris.load.sync.SyncJobManager; -import org.apache.doris.load.update.UpdateManager; import org.apache.doris.master.Checkpoint; import org.apache.doris.master.MetaHelper; import org.apache.doris.master.PartitionInMemoryInfoCollector; @@ -318,7 +317,6 @@ public class Env { private ConsistencyChecker consistencyChecker; private BackupHandler backupHandler; private PublishVersionDaemon publishVersionDaemon; - private UpdateManager updateManager; private DeleteHandler deleteHandler; private DbUsedDataQuotaInfoCollector dbUsedDataQuotaInfoCollector; private PartitionInMemoryInfoCollector partitionInMemoryInfoCollector; @@ -554,7 +552,6 @@ public class Env { this.backupHandler = new BackupHandler(this); this.metaDir = Config.meta_dir; this.publishVersionDaemon = new PublishVersionDaemon(); - this.updateManager = new UpdateManager(); this.deleteHandler = new DeleteHandler(); this.dbUsedDataQuotaInfoCollector = new DbUsedDataQuotaInfoCollector(); this.partitionInMemoryInfoCollector = new PartitionInMemoryInfoCollector(); @@ -3478,10 +3475,6 @@ public class Env { return this.backupHandler; } - public UpdateManager getUpdateManager() { - return updateManager; - } - public DeleteHandler getDeleteHandler() { return this.deleteHandler; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/load/update/UpdateManager.java b/fe/fe-core/src/main/java/org/apache/doris/load/update/UpdateManager.java deleted file mode 100644 index 697709fd4c..0000000000 --- a/fe/fe-core/src/main/java/org/apache/doris/load/update/UpdateManager.java +++ /dev/null @@ -1,89 +0,0 @@ -// 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.load.update; - -import org.apache.doris.analysis.UpdateStmt; -import org.apache.doris.common.AnalysisException; -import org.apache.doris.common.Config; -import org.apache.doris.common.DdlException; -import org.apache.doris.common.UserException; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -public class UpdateManager { - private final boolean enableConcurrentUpdate = Config.enable_concurrent_update; - private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); - private Map<Long, List<UpdateStmtExecutor>> tableIdToCurrentUpdate = Maps.newConcurrentMap(); - - private void writeLock() { - rwLock.writeLock().lock(); - } - - private void writeUnlock() { - rwLock.writeLock().unlock(); - } - - public void handleUpdate(UpdateStmt updateStmt) throws UserException { - UpdateStmtExecutor updateStmtExecutor = addUpdateExecutor(updateStmt); - try { - updateStmtExecutor.execute(); - } finally { - removeUpdateExecutor(updateStmtExecutor); - } - } - - private UpdateStmtExecutor addUpdateExecutor(UpdateStmt updateStmt) throws AnalysisException, DdlException { - writeLock(); - try { - List<UpdateStmtExecutor> currentUpdateList - = tableIdToCurrentUpdate.get(updateStmt.getTargetTable().getId()); - if (!enableConcurrentUpdate && currentUpdateList != null && currentUpdateList.size() > 0) { - throw new DdlException("There is an update operation in progress for the current table. " - + "Please try again later, or set enable_concurrent_update in fe.conf to true"); - } - UpdateStmtExecutor updateStmtExecutor = UpdateStmtExecutor.fromUpdateStmt(updateStmt); - if (currentUpdateList == null) { - currentUpdateList = Lists.newArrayList(); - tableIdToCurrentUpdate.put(updateStmtExecutor.getTargetTableId(), currentUpdateList); - } - currentUpdateList.add(updateStmtExecutor); - return updateStmtExecutor; - } finally { - writeUnlock(); - } - } - - private void removeUpdateExecutor(UpdateStmtExecutor updateStmtExecutor) { - writeLock(); - try { - List<UpdateStmtExecutor> currentUpdateList - = tableIdToCurrentUpdate.get(updateStmtExecutor.getTargetTableId()); - if (currentUpdateList == null) { - return; - } - currentUpdateList.remove(updateStmtExecutor); - } finally { - writeUnlock(); - } - } -} diff --git a/fe/fe-core/src/main/java/org/apache/doris/load/update/UpdatePlanner.java b/fe/fe-core/src/main/java/org/apache/doris/load/update/UpdatePlanner.java deleted file mode 100644 index d116477bcc..0000000000 --- a/fe/fe-core/src/main/java/org/apache/doris/load/update/UpdatePlanner.java +++ /dev/null @@ -1,192 +0,0 @@ -// 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.load.update; - -import org.apache.doris.alter.SchemaChangeHandler; -import org.apache.doris.analysis.Analyzer; -import org.apache.doris.analysis.BinaryPredicate; -import org.apache.doris.analysis.DescriptorTable; -import org.apache.doris.analysis.Expr; -import org.apache.doris.analysis.NullLiteral; -import org.apache.doris.analysis.SlotDescriptor; -import org.apache.doris.analysis.SlotRef; -import org.apache.doris.analysis.TupleDescriptor; -import org.apache.doris.catalog.Column; -import org.apache.doris.catalog.OlapTable; -import org.apache.doris.common.AnalysisException; -import org.apache.doris.common.IdGenerator; -import org.apache.doris.common.UserException; -import org.apache.doris.common.util.VectorizedUtil; -import org.apache.doris.planner.DataPartition; -import org.apache.doris.planner.OlapScanNode; -import org.apache.doris.planner.OlapTableSink; -import org.apache.doris.planner.OriginalPlanner; -import org.apache.doris.planner.PlanFragment; -import org.apache.doris.planner.PlanFragmentId; -import org.apache.doris.planner.PlanNodeId; -import org.apache.doris.planner.ScanNode; - -import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -import java.util.List; -import java.util.Map; - - -public class UpdatePlanner extends OriginalPlanner { - - private final IdGenerator<PlanNodeId> nodeIdGenerator = PlanNodeId.createGenerator(); - private final IdGenerator<PlanFragmentId> fragmentIdGenerator = - PlanFragmentId.createGenerator(); - - private long targetDBId; - private OlapTable targetTable; - private List<Expr> setExprs; - private TupleDescriptor srcTupleDesc; - private Analyzer analyzer; - - private List<ScanNode> scanNodeList = Lists.newArrayList(); - - public UpdatePlanner(long dbId, OlapTable targetTable, List<Expr> setExprs, - TupleDescriptor srcTupleDesc, Analyzer analyzer) { - super(analyzer); - this.targetDBId = dbId; - this.targetTable = targetTable; - this.setExprs = setExprs; - this.srcTupleDesc = srcTupleDesc; - this.analyzer = analyzer; - } - - @Override - public List<ScanNode> getScanNodes() { - return scanNodeList; - } - - public void plan(long txnId) throws UserException { - // 1. gen scan node - OlapScanNode olapScanNode = new OlapScanNode(nodeIdGenerator.getNextId(), srcTupleDesc, "OlapScanNode"); - /* BEGIN: Temporary code, this part of the code needs to be refactored */ - olapScanNode.closePreAggregation("This an update operation"); - olapScanNode.useBaseIndexId(); - /* END */ - olapScanNode.init(analyzer); - olapScanNode.finalize(analyzer); - if (VectorizedUtil.isVectorized()) { - olapScanNode.convertToVectorized(); - } - scanNodeList.add(olapScanNode); - // 2. gen olap table sink - OlapTableSink olapTableSink = new OlapTableSink(targetTable, computeTargetTupleDesc(), null, false); - olapTableSink.init(analyzer.getContext().queryId(), txnId, targetDBId, - analyzer.getContext().getSessionVariable().queryTimeoutS, - analyzer.getContext().getSessionVariable().sendBatchParallelism, false); - olapTableSink.complete(); - // 3. gen plan fragment - PlanFragment planFragment = new PlanFragment(fragmentIdGenerator.getNextId(), olapScanNode, - DataPartition.RANDOM); - planFragment.setSink(olapTableSink); - planFragment.setOutputExprs(computeOutputExprs()); - planFragment.finalize(null); - fragments.add(planFragment); - } - - private TupleDescriptor computeTargetTupleDesc() { - DescriptorTable descTable = analyzer.getDescTbl(); - TupleDescriptor targetTupleDesc = descTable.createTupleDescriptor(); - for (Column col : targetTable.getFullSchema()) { - SlotDescriptor slotDesc = descTable.addSlotDescriptor(targetTupleDesc); - slotDesc.setIsMaterialized(true); - slotDesc.setType(col.getType()); - slotDesc.setColumn(col); - slotDesc.setIsNullable(col.isAllowNull()); - } - targetTupleDesc.computeStatAndMemLayout(); - return targetTupleDesc; - } - - /** - * There are three Rules of output exprs: - * RuleA: columns that need to be updated, - * use the right child of a set expr - * base column: (k1, v1) - * update stmt: set v1=1 - * output expr: k1, 1(use 1 as output expr) - * RuleB: columns that do not need to be updated, - * just add the original value of column -> slot ref - * base column: (k1, v1) - * update stmt: set v1 = 1 - * output expr: k1(use k1 slot ref as output expr), 1 - * RuleC: the output columns is being added by the schema change job, - * need to add default value expr in output expr - * base column: (k1, v1) - * schema change job: add v2 column - * full column: (k1, v1, v2) - * output expr: k1, v1, default_value(v2) - */ - private List<Expr> computeOutputExprs() throws AnalysisException { - Map<String, Expr> columnNameToSetExpr = Maps.newHashMap(); - for (Expr setExpr : setExprs) { - Preconditions.checkState(setExpr instanceof BinaryPredicate); - Preconditions.checkState(setExpr.getChild(0) instanceof SlotRef); - SlotRef slotRef = (SlotRef) setExpr.getChild(0); - // pay attention to case ignore of column name - columnNameToSetExpr.put(slotRef.getColumnName().toLowerCase(), setExpr.getChild(1)); - } - Map<String, SlotDescriptor> columnNameToSrcSlotDesc = Maps.newHashMap(); - for (SlotDescriptor srcSlotDesc : srcTupleDesc.getSlots()) { - // pay attention to case ignore of column name - columnNameToSrcSlotDesc.put(srcSlotDesc.getColumn().getName().toLowerCase(), srcSlotDesc); - } - - // compute output expr - List<Expr> outputExprs = Lists.newArrayList(); - for (int i = 0; i < targetTable.getFullSchema().size(); i++) { - Column column = targetTable.getFullSchema().get(i); - // pay attention to case ignore of column name - String originColumnName = (column.getName().startsWith(SchemaChangeHandler.SHADOW_NAME_PREFIX) - ? column.getName().substring(SchemaChangeHandler.SHADOW_NAME_PREFIX.length()) : column.getName()) - .toLowerCase(); - Expr setExpr = columnNameToSetExpr.get(originColumnName); - SlotDescriptor srcSlotDesc = columnNameToSrcSlotDesc.get(originColumnName); - if (setExpr != null) { - // RuleA - outputExprs.add(setExpr); - } else if (srcSlotDesc != null) { - // RuleB - SlotRef slotRef = new SlotRef(srcSlotDesc); - outputExprs.add(slotRef); - } else { - // RuleC - Expr defaultExpr; - if (column.getDefaultValue() != null) { - defaultExpr = column.getDefaultValueExpr(); - } else { - if (column.isAllowNull()) { - defaultExpr = NullLiteral.create(column.getType()); - } else { - throw new AnalysisException("column has no source field, column=" + column.getName()); - } - } - defaultExpr.analyze(analyzer); - outputExprs.add(defaultExpr); - } - } - return outputExprs; - } -} diff --git a/fe/fe-core/src/main/java/org/apache/doris/load/update/UpdateStmtExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/load/update/UpdateStmtExecutor.java deleted file mode 100644 index f7148f9b15..0000000000 --- a/fe/fe-core/src/main/java/org/apache/doris/load/update/UpdateStmtExecutor.java +++ /dev/null @@ -1,238 +0,0 @@ -// 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.load.update; - -import org.apache.doris.analysis.Analyzer; -import org.apache.doris.analysis.Expr; -import org.apache.doris.analysis.UpdateStmt; -import org.apache.doris.catalog.Database; -import org.apache.doris.catalog.Env; -import org.apache.doris.catalog.OlapTable; -import org.apache.doris.common.AnalysisException; -import org.apache.doris.common.DdlException; -import org.apache.doris.common.DuplicatedRequestException; -import org.apache.doris.common.ErrorCode; -import org.apache.doris.common.ErrorReport; -import org.apache.doris.common.LabelAlreadyUsedException; -import org.apache.doris.common.MetaNotFoundException; -import org.apache.doris.common.QuotaExceedException; -import org.apache.doris.common.UserException; -import org.apache.doris.common.util.DebugUtil; -import org.apache.doris.common.util.TimeUtils; -import org.apache.doris.common.util.VectorizedUtil; -import org.apache.doris.qe.Coordinator; -import org.apache.doris.qe.QeProcessorImpl; -import org.apache.doris.service.FrontendOptions; -import org.apache.doris.task.LoadEtlTask; -import org.apache.doris.thrift.TQueryType; -import org.apache.doris.thrift.TUniqueId; -import org.apache.doris.transaction.BeginTransactionException; -import org.apache.doris.transaction.GlobalTransactionMgr; -import org.apache.doris.transaction.TabletCommitInfo; -import org.apache.doris.transaction.TransactionState.LoadJobSourceType; -import org.apache.doris.transaction.TransactionState.TxnCoordinator; -import org.apache.doris.transaction.TransactionState.TxnSourceType; -import org.apache.doris.transaction.TransactionStatus; - -import com.google.common.collect.Lists; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.List; - -public class UpdateStmtExecutor { - private static final Logger LOG = LogManager.getLogger(UpdateStmtExecutor.class); - - private OlapTable targetTable; - private Expr whereExpr; - private List<Expr> setExprs; - private long dbId; - private TUniqueId queryId; - private int timeoutSecond; - private Analyzer analyzer; - private UpdatePlanner updatePlanner; - - private String label; - private long txnId; - private Coordinator coordinator; - private long effectRows; - - - public long getTargetTableId() { - return targetTable.getId(); - } - - public void execute() throws UserException { - // 0. empty set - // A where clause with a constant equal to false will not execute the update directly - // Example: update xxx set v1=0 where 1=2 - if (analyzer.hasEmptyResultSet()) { - QeProcessorImpl.INSTANCE.unregisterQuery(queryId); - analyzer.getContext().getState().setOk(); - return; - } - - // 1. begin txn - beginTxn(); - - // 2. plan - targetTable.readLock(); - try { - updatePlanner.plan(txnId); - } catch (Throwable e) { - LOG.warn("failed to plan update stmt, query id:{}", DebugUtil.printId(queryId), e); - Env.getCurrentGlobalTransactionMgr().abortTransaction(dbId, txnId, e.getMessage()); - QeProcessorImpl.INSTANCE.unregisterQuery(queryId); - throw new DdlException("failed to plan update stmt, query id: " - + DebugUtil.printId(queryId) + ", err: " + e.getMessage()); - } finally { - targetTable.readUnlock(); - } - - // 3. execute plan - try { - executePlan(); - } catch (DdlException e) { - LOG.warn("failed to execute update stmt, query id:{}", DebugUtil.printId(queryId), e); - Env.getCurrentGlobalTransactionMgr().abortTransaction(dbId, txnId, e.getMessage()); - throw e; - } catch (Throwable e) { - LOG.warn("failed to execute update stmt, query id:{}", DebugUtil.printId(queryId), e); - Env.getCurrentGlobalTransactionMgr().abortTransaction(dbId, txnId, e.getMessage()); - throw new DdlException("failed to execute update stmt, query id: " - + DebugUtil.printId(queryId) + ", err: " + e.getMessage()); - } finally { - QeProcessorImpl.INSTANCE.unregisterQuery(queryId); - } - - // 4. commit and publish - commitAndPublishTxn(); - } - - private void beginTxn() throws LabelAlreadyUsedException, AnalysisException, BeginTransactionException, - DuplicatedRequestException, QuotaExceedException, MetaNotFoundException { - LOG.info("begin transaction for update stmt, query id:{}", DebugUtil.printId(queryId)); - label = "update_" + DebugUtil.printId(queryId); - txnId = Env.getCurrentGlobalTransactionMgr() - .beginTransaction(dbId, Lists.newArrayList(targetTable.getId()), label, - new TxnCoordinator(TxnSourceType.FE, FrontendOptions.getLocalHostAddress()), - LoadJobSourceType.INSERT_STREAMING, timeoutSecond); - } - - // TODO(ML): Abstract the logic of executing the coordinater and retrying. - // It makes stmt such as insert, load, update and export can be reused - private void executePlan() throws Exception { - LOG.info("begin execute update stmt, query id:{}", DebugUtil.printId(queryId)); - coordinator = new Coordinator(Env.getCurrentEnv().getNextId(), queryId, analyzer.getDescTbl(), - updatePlanner.getFragments(), updatePlanner.getScanNodes(), TimeUtils.DEFAULT_TIME_ZONE, false); - coordinator.setQueryType(TQueryType.LOAD); - coordinator.setExecVecEngine(VectorizedUtil.isVectorized()); - coordinator.setExecPipEngine(VectorizedUtil.isPipeline()); - QeProcessorImpl.INSTANCE.registerQuery(queryId, coordinator); - analyzer.getContext().getExecutor().setCoord(coordinator); - - // execute - coordinator.setTimeout(timeoutSecond); - coordinator.exec(); - if (coordinator.join(timeoutSecond)) { - if (!coordinator.isDone()) { - coordinator.cancel(); - ErrorReport.reportDdlException(ErrorCode.ERR_EXECUTE_TIMEOUT); - } - if (!coordinator.getExecStatus().ok()) { - String errMsg = "update failed: " + coordinator.getExecStatus().getErrorMsg(); - LOG.warn(errMsg); - throw new DdlException(errMsg); - } - LOG.info("finish to execute update stmt, query id:{}", DebugUtil.printId(queryId)); - } else { - String errMsg = "coordinator could not finished before update timeout: " - + coordinator.getExecStatus().getErrorMsg(); - LOG.warn(errMsg); - throw new DdlException(errMsg); - } - - // counter - if (coordinator.getLoadCounters().get(LoadEtlTask.DPP_NORMAL_ALL) != null) { - effectRows = Long.valueOf(coordinator.getLoadCounters().get(LoadEtlTask.DPP_NORMAL_ALL)); - if (Long.valueOf(coordinator.getLoadCounters().get(LoadEtlTask.DPP_ABNORMAL_ALL)) != 0) { - throw new DdlException("update failed, some rows did not take effect"); - } - } - } - - private void commitAndPublishTxn() throws UserException { - GlobalTransactionMgr globalTransactionMgr = Env.getCurrentGlobalTransactionMgr(); - TransactionStatus txnStatus; - boolean isPublished; - try { - LOG.info("commit and publish transaction for update stmt, query id: {}", DebugUtil.printId(queryId)); - isPublished = globalTransactionMgr.commitAndPublishTransaction( - Env.getCurrentInternalCatalog().getDbOrMetaException(dbId), - Lists.newArrayList(targetTable), txnId, TabletCommitInfo.fromThrift(coordinator.getCommitInfos()), - analyzer.getContext().getSessionVariable().getInsertVisibleTimeoutMs()); - } catch (Throwable e) { - // situation2.1: publish error, throw exception - String errMsg = "failed to commit and publish transaction for update stmt, query id:" - + DebugUtil.printId(queryId); - LOG.warn(errMsg, e); - globalTransactionMgr.abortTransaction(dbId, txnId, e.getMessage()); - throw new DdlException(errMsg, e); - } - String errMsg = null; - if (isPublished) { - // situation2.2: publish successful - txnStatus = TransactionStatus.VISIBLE; - } else { - // situation2.3: be published later - txnStatus = TransactionStatus.COMMITTED; - errMsg = "transaction will be published later, data will be visible later"; - LOG.warn("transaction will be published later, query id: {}", DebugUtil.printId(queryId)); - } - - // set context - StringBuilder sb = new StringBuilder(); - sb.append("{'label':'").append(label).append("', 'status':'").append(txnStatus.name()).append("'"); - sb.append(", 'txnId':'").append(txnId).append("'"); - sb.append(", 'queryId':'").append(DebugUtil.printId(queryId)).append("'"); - if (errMsg != null) { - sb.append(", 'err':'").append(errMsg).append("'"); - } - sb.append("}"); - analyzer.getContext().getState().setOk(effectRows, 0, sb.toString()); - } - - public static UpdateStmtExecutor fromUpdateStmt(UpdateStmt updateStmt) throws AnalysisException { - UpdateStmtExecutor updateStmtExecutor = new UpdateStmtExecutor(); - updateStmtExecutor.targetTable = (OlapTable) updateStmt.getTargetTable(); - updateStmtExecutor.whereExpr = updateStmt.getWhereExpr(); - updateStmtExecutor.setExprs = updateStmt.getSetExprs(); - Database database = Env.getCurrentInternalCatalog() - .getDbOrAnalysisException(updateStmt.getTableName().getDb()); - updateStmtExecutor.dbId = database.getId(); - updateStmtExecutor.analyzer = updateStmt.getAnalyzer(); - updateStmtExecutor.queryId = updateStmtExecutor.analyzer.getContext().queryId(); - updateStmtExecutor.timeoutSecond = updateStmtExecutor.analyzer.getContext() - .getExecTimeout(); - updateStmtExecutor.updatePlanner = new UpdatePlanner(updateStmtExecutor.dbId, updateStmtExecutor.targetTable, - updateStmt.getSetExprs(), updateStmt.getSrcTupleDesc(), - updateStmt.getAnalyzer()); - return updateStmtExecutor; - } - -} diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java index 4d0e873643..e4df13fb05 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java @@ -110,7 +110,6 @@ import org.apache.doris.analysis.StopSyncJobStmt; import org.apache.doris.analysis.SyncStmt; import org.apache.doris.analysis.TruncateTableStmt; import org.apache.doris.analysis.UninstallPluginStmt; -import org.apache.doris.analysis.UpdateStmt; import org.apache.doris.catalog.EncryptKeyHelper; import org.apache.doris.catalog.Env; import org.apache.doris.common.DdlException; @@ -182,8 +181,6 @@ public class DdlExecutor { env.getRoutineLoadManager().stopRoutineLoadJob((StopRoutineLoadStmt) ddlStmt); } else if (ddlStmt instanceof AlterRoutineLoadStmt) { env.getRoutineLoadManager().alterRoutineLoadJob((AlterRoutineLoadStmt) ddlStmt); - } else if (ddlStmt instanceof UpdateStmt) { - env.getUpdateManager().handleUpdate((UpdateStmt) ddlStmt); } else if (ddlStmt instanceof DeleteStmt) { env.getDeleteHandler().process((DeleteStmt) ddlStmt); } else if (ddlStmt instanceof CreateUserStmt) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java index 8dfd08a05e..1b52bc4574 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java @@ -30,6 +30,7 @@ import org.apache.doris.thrift.TRuntimeFilterType; import com.google.common.base.Joiner; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.apache.logging.log4j.LogManager; @@ -290,6 +291,14 @@ public class SessionVariable implements Serializable, Writable { public static final String DRY_RUN_QUERY = "dry_run_query"; + public static final List<String> DEBUG_VARIABLES = ImmutableList.of( + SKIP_DELETE_PREDICATE, + SKIP_DELETE_BITMAP, + SKIP_DELETE_SIGN, + SKIP_STORAGE_ENGINE_MERGE, + SHOW_HIDDEN_COLUMNS + ); + // session origin value public Map<Field, String> sessionOriginValue = new HashMap<Field, String>(); // check stmt is or not [select /*+ SET_VAR(...)*/ ...] @@ -858,6 +867,10 @@ public class SessionVariable implements Serializable, Writable { private Set<Class<? extends Event>> parsedNereidsEventMode = EventSwitchParser.parse(Lists.newArrayList("all")); + public boolean isInDebugMode() { + return showHiddenColumns || skipDeleteBitmap || skipDeletePredicate || skipDeleteSign || skipStorageEngineMerge; + } + public void setEnableNereidsTrace(boolean enableNereidsTrace) { this.enableNereidsTrace = enableNereidsTrace; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java index 97532558e9..c5e9f8caf2 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java @@ -59,6 +59,7 @@ import org.apache.doris.analysis.TransactionRollbackStmt; import org.apache.doris.analysis.TransactionStmt; import org.apache.doris.analysis.UnlockTablesStmt; import org.apache.doris.analysis.UnsupportedStmt; +import org.apache.doris.analysis.UpdateStmt; import org.apache.doris.analysis.UseStmt; import org.apache.doris.catalog.Column; import org.apache.doris.catalog.Database; @@ -605,6 +606,8 @@ public class StmtExecutor implements ProfileWriter { } } else if (parsedStmt instanceof LoadStmt) { handleLoadStmt(); + } else if (parsedStmt instanceof UpdateStmt) { + handleUpdateStmt(); } else if (parsedStmt instanceof DdlStmt) { handleDdlStmt(); } else if (parsedStmt instanceof ShowStmt) { @@ -1925,6 +1928,19 @@ public class StmtExecutor implements ProfileWriter { } } + private void handleUpdateStmt() { + try { + UpdateStmt updateStmt = (UpdateStmt) parsedStmt; + parsedStmt = updateStmt.getInsertStmt(); + execute(); + if (MysqlStateType.ERR.equals(context.getState().getStateType())) { + LOG.warn("update data error, stmt={}", parsedStmt.toSql()); + } + } catch (Exception e) { + LOG.warn("update data error, stmt={}", parsedStmt.toSql(), e); + } + } + private void handleDdlStmt() { try { DdlExecutor.execute(context.getEnv(), (DdlStmt) parsedStmt); diff --git a/fe/fe-core/src/test/java/org/apache/doris/load/update/UpdateManagerTest.java b/fe/fe-core/src/test/java/org/apache/doris/load/update/UpdateManagerTest.java deleted file mode 100644 index 5d48093ad4..0000000000 --- a/fe/fe-core/src/test/java/org/apache/doris/load/update/UpdateManagerTest.java +++ /dev/null @@ -1,66 +0,0 @@ -// 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.load.update; - -import org.apache.doris.analysis.UpdateStmt; -import org.apache.doris.common.Config; -import org.apache.doris.common.DdlException; -import org.apache.doris.common.jmockit.Deencapsulation; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import mockit.Expectations; -import mockit.Injectable; -import org.junit.Assert; -import org.junit.Test; - -import java.util.List; -import java.util.Map; - -public class UpdateManagerTest { - - @Test - public void testDisableConcurrentUpdate(@Injectable UpdateStmt updateStmt, - @Injectable UpdateStmtExecutor updateStmtExecutor) { - Config.enable_concurrent_update = false; - Map<Long, List<UpdateStmtExecutor>> tableIdToCurrentUpdate = Maps.newConcurrentMap(); - List<UpdateStmtExecutor> currentUpdate = Lists.newArrayList(); - currentUpdate.add(updateStmtExecutor); - tableIdToCurrentUpdate.put(new Long(1), currentUpdate); - UpdateManager updateManager = new UpdateManager(); - Assert.assertFalse(Deencapsulation.getField(updateManager, "enableConcurrentUpdate")); - Deencapsulation.setField(updateManager, "tableIdToCurrentUpdate", tableIdToCurrentUpdate); - new Expectations() { - { - updateStmt.getTargetTable().getId(); - result = 1; - } - }; - - try { - Deencapsulation.invoke(updateManager, "addUpdateExecutor", updateStmt); - Assert.fail(); - } catch (Exception e) { - if (e instanceof DdlException) { - System.out.println(e.getMessage()); - } else { - throw e; - } - } - } -} diff --git a/fe/fe-core/src/test/java/org/apache/doris/load/update/UpdateStmtExecutorTest.java b/fe/fe-core/src/test/java/org/apache/doris/load/update/UpdateStmtExecutorTest.java deleted file mode 100644 index 2fe3b13e20..0000000000 --- a/fe/fe-core/src/test/java/org/apache/doris/load/update/UpdateStmtExecutorTest.java +++ /dev/null @@ -1,101 +0,0 @@ -// 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.load.update; - -import org.apache.doris.analysis.Analyzer; -import org.apache.doris.analysis.BinaryPredicate; -import org.apache.doris.analysis.Expr; -import org.apache.doris.analysis.IntLiteral; -import org.apache.doris.analysis.SlotRef; -import org.apache.doris.analysis.TableName; -import org.apache.doris.analysis.UpdateStmt; -import org.apache.doris.catalog.Database; -import org.apache.doris.catalog.Env; -import org.apache.doris.catalog.OlapTable; -import org.apache.doris.cluster.Cluster; -import org.apache.doris.common.AnalysisException; -import org.apache.doris.common.jmockit.Deencapsulation; -import org.apache.doris.datasource.InternalCatalog; -import org.apache.doris.qe.Coordinator; -import org.apache.doris.thrift.TUniqueId; -import org.apache.doris.transaction.GlobalTransactionMgr; - -import com.google.common.collect.Lists; -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mocked; -import org.junit.Assert; -import org.junit.Test; - -import java.util.List; - -public class UpdateStmtExecutorTest { - - @Test - public void testCommitAndPublishTxn(@Injectable Analyzer analyzer, - @Injectable Coordinator coordinator, - @Mocked GlobalTransactionMgr globalTransactionMgr) { - Cluster testCluster = new Cluster("test_cluster", 0); - Database testDb = new Database(1, "test_db"); - testDb.setClusterName("test_cluster"); - Env.getCurrentEnv().addCluster(testCluster); - Env.getCurrentEnv().unprotectCreateDb(testDb); - UpdateStmtExecutor updateStmtExecutor = new UpdateStmtExecutor(); - Deencapsulation.setField(updateStmtExecutor, "dbId", 1); - Deencapsulation.setField(updateStmtExecutor, "effectRows", 0); - Deencapsulation.setField(updateStmtExecutor, "analyzer", analyzer); - Deencapsulation.setField(updateStmtExecutor, "coordinator", coordinator); - Deencapsulation.invoke(updateStmtExecutor, "commitAndPublishTxn"); - } - - @Test - public void testFromUpdateStmt(@Injectable OlapTable olapTable, - @Mocked Env env, - @Injectable Database db, - @Injectable Analyzer analyzer) throws AnalysisException { - TableName tableName = new TableName(InternalCatalog.INTERNAL_CATALOG_NAME, "db", "test"); - List<Expr> setExprs = Lists.newArrayList(); - SlotRef slotRef = new SlotRef(tableName, "v1"); - IntLiteral intLiteral = new IntLiteral(1); - BinaryPredicate binaryPredicate = new BinaryPredicate(BinaryPredicate.Operator.EQ, - slotRef, intLiteral); - setExprs.add(binaryPredicate); - SlotRef keySlotRef = new SlotRef(tableName, "k1"); - Expr whereExpr = new BinaryPredicate(BinaryPredicate.Operator.EQ, keySlotRef, intLiteral); - UpdateStmt updateStmt = new UpdateStmt(tableName, setExprs, whereExpr); - Deencapsulation.setField(updateStmt, "targetTable", olapTable); - Deencapsulation.setField(updateStmt, "analyzer", analyzer); - new Expectations() { - { - db.getId(); - result = 1; - analyzer.getContext().queryId(); - result = new TUniqueId(1, 2); - analyzer.getContext().getExecTimeout(); - result = 1000; - olapTable.getId(); - result = 2; - } - }; - UpdateStmtExecutor executor = UpdateStmtExecutor.fromUpdateStmt(updateStmt); - Assert.assertEquals(new Long(2), new Long(executor.getTargetTableId())); - Assert.assertEquals(whereExpr, Deencapsulation.getField(executor, "whereExpr")); - Assert.assertEquals(setExprs, Deencapsulation.getField(executor, "setExprs")); - Assert.assertEquals(new Long(1), Deencapsulation.getField(executor, "dbId")); - } -} diff --git a/fe/fe-core/src/test/java/org/apache/doris/planner/PlannerTest.java b/fe/fe-core/src/test/java/org/apache/doris/planner/PlannerTest.java index 04302e1c62..730aab5674 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/planner/PlannerTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/planner/PlannerTest.java @@ -494,16 +494,8 @@ public class PlannerTest extends TestWithFeService { Assertions.assertEquals(MysqlStateType.ERR, state.getStateType()); Assertions.assertTrue(state.getErrorMessage() .contains("you need (at least one of) the LOAD privilege(s) for this operation")); - // set to admin user connectContext.setCurrentUserIdentity(UserIdentity.ADMIN); - stmtExecutor = new StmtExecutor(connectContext, qSQL); - stmtExecutor.execute(); - state = connectContext.getState(); - // still error because we can not do real update in unit test. - // just check if it pass the priv check. - Assertions.assertEquals(MysqlStateType.ERR, state.getStateType()); - Assertions.assertTrue(state.getErrorMessage().contains("failed to execute update stmt")); } @Test diff --git a/fe/fe-core/src/test/java/org/apache/doris/planner/UpdatePlannerTest.java b/fe/fe-core/src/test/java/org/apache/doris/planner/UpdatePlannerTest.java deleted file mode 100644 index 253ce82930..0000000000 --- a/fe/fe-core/src/test/java/org/apache/doris/planner/UpdatePlannerTest.java +++ /dev/null @@ -1,185 +0,0 @@ -// 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.planner; - -import org.apache.doris.alter.SchemaChangeHandler; -import org.apache.doris.analysis.Analyzer; -import org.apache.doris.analysis.BinaryPredicate; -import org.apache.doris.analysis.Expr; -import org.apache.doris.analysis.IntLiteral; -import org.apache.doris.analysis.SlotDescriptor; -import org.apache.doris.analysis.SlotId; -import org.apache.doris.analysis.SlotRef; -import org.apache.doris.analysis.TableName; -import org.apache.doris.analysis.TupleDescriptor; -import org.apache.doris.analysis.TupleId; -import org.apache.doris.catalog.Column; -import org.apache.doris.catalog.OlapTable; -import org.apache.doris.common.AnalysisException; -import org.apache.doris.common.IdGenerator; -import org.apache.doris.common.jmockit.Deencapsulation; -import org.apache.doris.datasource.InternalCatalog; -import org.apache.doris.load.update.UpdatePlanner; - -import com.google.common.collect.Lists; -import mockit.Expectations; -import mockit.Injectable; -import org.junit.Assert; -import org.junit.Test; - -import java.util.List; - -public class UpdatePlannerTest { - - private final IdGenerator<TupleId> tupleIdGenerator = TupleId.createGenerator(); - private final IdGenerator<SlotId> slotIdGenerator = SlotId.createGenerator(); - - /** - * Full columns: k1, k2 v1, shadow_column - * Shadow column: SHADOW_NAME_PRFIX + v1 - * Set expr: v1=1 - * Expect output exprs: k1, k2, 1, 1 - */ - @Test - public void testComputeOutputExprsWithShadowColumnAndSetExpr(@Injectable OlapTable targetTable, - @Injectable Column k1, - @Injectable Column k2, - @Injectable Column v1, - @Injectable Column shadowV1, - @Injectable Analyzer analyzer) { - List<Expr> setExprs = Lists.newArrayList(); - TableName tableName = new TableName(InternalCatalog.INTERNAL_CATALOG_NAME, null, "test"); - SlotRef slotRef = new SlotRef(tableName, "V1"); - IntLiteral intLiteral = new IntLiteral(1); - BinaryPredicate binaryPredicate = new BinaryPredicate(BinaryPredicate.Operator.EQ, - slotRef, intLiteral); - setExprs.add(binaryPredicate); - TupleDescriptor srcTupleDesc = new TupleDescriptor(tupleIdGenerator.getNextId()); - SlotDescriptor k1SlotDesc = new SlotDescriptor(slotIdGenerator.getNextId(), srcTupleDesc); - k1SlotDesc.setColumn(k1); - srcTupleDesc.addSlot(k1SlotDesc); - SlotDescriptor k2SlotDesc = new SlotDescriptor(slotIdGenerator.getNextId(), srcTupleDesc); - k2SlotDesc.setColumn(k2); - srcTupleDesc.addSlot(k2SlotDesc); - SlotDescriptor v1SlotDesc = new SlotDescriptor(slotIdGenerator.getNextId(), srcTupleDesc); - v1SlotDesc.setColumn(v1); - srcTupleDesc.addSlot(v1SlotDesc); - List<Column> fullSchema = Lists.newArrayList(); - fullSchema.add(k1); - fullSchema.add(k2); - fullSchema.add(v1); - fullSchema.add(shadowV1); - - new Expectations() { - { - targetTable.getFullSchema(); - result = fullSchema; - k1.getName(); - result = "k1"; - k2.getName(); - result = "k2"; - v1.getName(); - result = "v1"; - shadowV1.getName(); - result = SchemaChangeHandler.SHADOW_NAME_PREFIX + "v1"; - } - }; - - UpdatePlanner updatePlanner = new UpdatePlanner(1, targetTable, setExprs, srcTupleDesc, analyzer); - List<Expr> outputExpr = Deencapsulation.invoke(updatePlanner, "computeOutputExprs"); - Assert.assertEquals(4, outputExpr.size()); - Expr outputExpr1 = outputExpr.get(0); - Assert.assertTrue(outputExpr1 instanceof SlotRef); - Assert.assertEquals(((SlotRef) outputExpr1).getDesc().getColumn().getName(), "k1"); - Expr outputExpr2 = outputExpr.get(1); - Assert.assertTrue(outputExpr2 instanceof SlotRef); - Assert.assertEquals(((SlotRef) outputExpr2).getDesc().getColumn().getName(), "k2"); - Expr outputExpr3 = outputExpr.get(2); - Assert.assertTrue(outputExpr3 instanceof IntLiteral); - Assert.assertEquals(((IntLiteral) outputExpr3).getValue(), 1); - Expr outputExpr4 = outputExpr.get(3); - Assert.assertTrue(outputExpr4 instanceof IntLiteral); - Assert.assertEquals(((IntLiteral) outputExpr4).getValue(), 1); - } - - @Test - public void testNewColumnBySchemaChange(@Injectable OlapTable targetTable, - @Injectable Column k1, - @Injectable Column k2, - @Injectable Column v1, - @Injectable Column newV2, - @Injectable Analyzer analyzer) throws AnalysisException { - List<Expr> setExprs = Lists.newArrayList(); - TableName tableName = new TableName(InternalCatalog.INTERNAL_CATALOG_NAME, null, "test"); - SlotRef slotRef = new SlotRef(tableName, "V1"); - IntLiteral intLiteral = new IntLiteral(1); - BinaryPredicate binaryPredicate = new BinaryPredicate(BinaryPredicate.Operator.EQ, - slotRef, intLiteral); - setExprs.add(binaryPredicate); - TupleDescriptor srcTupleDesc = new TupleDescriptor(tupleIdGenerator.getNextId()); - SlotDescriptor k1SlotDesc = new SlotDescriptor(slotIdGenerator.getNextId(), srcTupleDesc); - k1SlotDesc.setColumn(k1); - srcTupleDesc.addSlot(k1SlotDesc); - SlotDescriptor k2SlotDesc = new SlotDescriptor(slotIdGenerator.getNextId(), srcTupleDesc); - k2SlotDesc.setColumn(k2); - srcTupleDesc.addSlot(k2SlotDesc); - SlotDescriptor v1SlotDesc = new SlotDescriptor(slotIdGenerator.getNextId(), srcTupleDesc); - v1SlotDesc.setColumn(v1); - srcTupleDesc.addSlot(v1SlotDesc); - List<Column> fullSchema = Lists.newArrayList(); - fullSchema.add(k1); - fullSchema.add(k2); - fullSchema.add(v1); - fullSchema.add(newV2); - - new Expectations() { - { - targetTable.getFullSchema(); - result = fullSchema; - k1.getName(); - result = "k1"; - k2.getName(); - result = "k2"; - v1.getName(); - result = "v1"; - newV2.getName(); - result = "v2"; - newV2.getDefaultValue(); - result = "1"; - newV2.getDefaultValueExpr(); - result = new IntLiteral(1); - } - }; - - UpdatePlanner updatePlanner = new UpdatePlanner(1, targetTable, setExprs, srcTupleDesc, analyzer); - List<Expr> outputExpr = Deencapsulation.invoke(updatePlanner, "computeOutputExprs"); - Assert.assertEquals(4, outputExpr.size()); - Expr outputExpr1 = outputExpr.get(0); - Assert.assertTrue(outputExpr1 instanceof SlotRef); - Assert.assertEquals(((SlotRef) outputExpr1).getDesc().getColumn().getName(), "k1"); - Expr outputExpr2 = outputExpr.get(1); - Assert.assertTrue(outputExpr2 instanceof SlotRef); - Assert.assertEquals(((SlotRef) outputExpr2).getDesc().getColumn().getName(), "k2"); - Expr outputExpr3 = outputExpr.get(2); - Assert.assertTrue(outputExpr3 instanceof IntLiteral); - Assert.assertEquals(((IntLiteral) outputExpr3).getValue(), 1); - Expr outputExpr4 = outputExpr.get(3); - Assert.assertTrue(outputExpr4 instanceof IntLiteral); - Assert.assertEquals(((IntLiteral) outputExpr4).getValue(), 1); - } -} diff --git a/regression-test/data/update/test_update_unique.out b/regression-test/data/update/test_update_unique.out index 98f001495e..3d8737af02 100644 --- a/regression-test/data/update/test_update_unique.out +++ b/regression-test/data/update/test_update_unique.out @@ -13,3 +13,8 @@ value1 INT Yes false \N REPLACE value2 INT Yes false \N REPLACE date_value DATE Yes false \N REPLACE +-- !complex_update -- +1 10 1 1000.0 2000-01-01 +2 2 2 2.0 2000-01-02 +3 3 3 3.0 2000-01-03 + diff --git a/regression-test/suites/update/test_update_unique.groovy b/regression-test/suites/update/test_update_unique.groovy index 21d2d26b13..6acff99f39 100644 --- a/regression-test/suites/update/test_update_unique.groovy +++ b/regression-test/suites/update/test_update_unique.groovy @@ -16,10 +16,12 @@ // under the License. suite("test_update_unique", "p0") { - def tbName = "test_update_unique" - sql "DROP TABLE IF EXISTS ${tbName}" + def tbName1 = "test_update_unique_1" + def tbName2 = "test_update_unique_2" + def tbName3 = "test_update_unique_3" + sql "DROP TABLE IF EXISTS ${tbName1}" sql """ - CREATE TABLE IF NOT EXISTS ${tbName} ( + CREATE TABLE IF NOT EXISTS ${tbName1} ( k int, value1 int, value2 int, @@ -28,14 +30,50 @@ suite("test_update_unique", "p0") { UNIQUE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 5 properties("replication_num" = "1"); """ - sql "insert into ${tbName} values(1, 1, 1, '2000-01-01');" - sql "insert into ${tbName} values(2, 1, 1, '2000-01-01');" - sql "UPDATE ${tbName} SET value1 = 2 WHERE k=1;" - sql "UPDATE ${tbName} SET value1 = value1+1 WHERE k=2;" - sql "UPDATE ${tbName} SET date_value = '1999-01-01' WHERE k in (1,2);" - qt_select_uniq_table "select * from ${tbName} order by k" - sql "UPDATE ${tbName} SET date_value = '1998-01-01' WHERE k is null or k is not null;" - qt_select_uniq_table "select * from ${tbName} order by k" - qt_desc_uniq_table "desc ${tbName}" - sql "DROP TABLE ${tbName}" + sql "insert into ${tbName1} values(1, 1, 1, '2000-01-01');" + sql "insert into ${tbName1} values(2, 1, 1, '2000-01-01');" + sql "UPDATE ${tbName1} SET value1 = 2 WHERE k=1;" + sql "UPDATE ${tbName1} SET value1 = value1+1 WHERE k=2;" + sql "UPDATE ${tbName1} SET date_value = '1999-01-01' WHERE k in (1,2);" + qt_select_uniq_table "select * from ${tbName1} order by k" + sql "UPDATE ${tbName1} SET date_value = '1998-01-01' WHERE k is null or k is not null;" + qt_select_uniq_table "select * from ${tbName1} order by k" + qt_desc_uniq_table "desc ${tbName1}" + sql "DROP TABLE ${tbName1}" + + sql "DROP TABLE IF EXISTS ${tbName1}" + sql "DROP TABLE IF EXISTS ${tbName2}" + sql "DROP TABLE IF EXISTS ${tbName3}" + + // test complex update syntax + sql """ + create table ${tbName1} (id int, c1 bigint, c2 string, c3 double, c4 date) unique key (id) distributed by hash(id) properties('replication_num'='1'); + """ + sql """ + create table ${tbName2} (id int, c1 bigint, c2 string, c3 double, c4 date) unique key (id) distributed by hash(id) properties('replication_num'='1'); + """ + sql """ + create table ${tbName3} (id int) distributed by hash (id) properties('replication_num'='1'); + """ + sql """ + insert into ${tbName1} values(1, 1, '1', 1.0, '2000-01-01'),(2, 2, '2', 2.0, '2000-01-02'),(3, 3, '3', 3.0, '2000-01-03'); + """ + sql """ + insert into ${tbName2} 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 ${tbName3} values(1), (4), (5); + """ + + sql """ + update ${tbName1} set ${tbName1}.c1 = ${tbName2}.c1, ${tbName1}.c3 = ${tbName2}.c3 * 100 from ${tbName2} inner join ${tbName3} on ${tbName2}.id = ${tbName3}.id where ${tbName1}.id = ${tbName2}.id; + """ + + qt_complex_update """ + select * from ${tbName1} order by id; + """ + + sql "DROP TABLE IF EXISTS ${tbName1}" + sql "DROP TABLE IF EXISTS ${tbName2}" + sql "DROP TABLE IF EXISTS ${tbName3}" } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org