diff --git a/src/exec/CMakeLists.txt b/src/exec/CMakeLists.txt
index e6c409f642dd330a9408b17968ecaefb2dbc9907..ab8ca5069f3c4a6c63ebf7f8fb63669fd1437d69 100644
--- a/src/exec/CMakeLists.txt
+++ b/src/exec/CMakeLists.txt
@@ -33,6 +33,7 @@ nebula_add_library(
     maintain/EdgeExecutor.cpp
     mutate/InsertExecutor.cpp
     mutate/DeleteExecutor.cpp
+    mutate/UpdateExecutor.cpp
 )
 
 nebula_add_subdirectory(query/test)
diff --git a/src/exec/Executor.cpp b/src/exec/Executor.cpp
index 66ffcbc8138a5d09efa75aba3ec7fbc0cb58b391..d7c4cc120568da08e18895761e3e3e820002fb93 100644
--- a/src/exec/Executor.cpp
+++ b/src/exec/Executor.cpp
@@ -24,6 +24,7 @@
 #include "exec/maintain/TagExecutor.h"
 #include "exec/mutate/InsertExecutor.h"
 #include "exec/mutate/DeleteExecutor.h"
+#include "exec/mutate/UpdateExecutor.h"
 #include "exec/query/AggregateExecutor.h"
 #include "exec/query/DataCollectExecutor.h"
 #include "exec/query/DataJoinExecutor.h"
@@ -384,6 +385,20 @@ Executor *Executor::makeExecutor(const PlanNode *node,
             exec->dependsOn(input);
             break;
         }
+        case PlanNode::Kind::kUpdateVertex: {
+            auto updateV = asNode<UpdateVertex>(node);
+            auto input = makeExecutor(updateV->dep(), qctx, visited);
+            exec = new UpdateVertexExecutor(updateV, qctx);
+            exec->dependsOn(input);
+            break;
+        }
+        case PlanNode::Kind::kUpdateEdge: {
+            auto updateE = asNode<UpdateEdge>(node);
+            auto input = makeExecutor(updateE->dep(), qctx, visited);
+            exec = new UpdateEdgeExecutor(updateE, qctx);
+            exec->dependsOn(input);
+            break;
+        }
         case PlanNode::Kind::kUnknown:
         default:
             LOG(FATAL) << "Unknown plan node kind " << static_cast<int32_t>(node->kind());
diff --git a/src/exec/mutate/UpdateExecutor.cpp b/src/exec/mutate/UpdateExecutor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d00a25862df50fac1291c5921c81af89567af7ab
--- /dev/null
+++ b/src/exec/mutate/UpdateExecutor.cpp
@@ -0,0 +1,169 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#include "UpdateExecutor.h"
+#include "planner/Mutate.h"
+#include "util/SchemaUtil.h"
+#include "context/QueryContext.h"
+#include "util/ScopedTimer.h"
+
+
+namespace nebula {
+namespace graph {
+
+StatusOr<DataSet> UpdateBaseExecutor::handleResult(DataSet &&data) {
+    if (data.colNames.size() <= 1) {
+        if (yieldNames_.empty()) {
+            return Status::OK();
+        }
+        LOG(ERROR) << "Empty return props";
+        return Status::Error("Empty return props");
+    }
+
+    if (yieldNames_.size() != data.colNames.size() - 1) {
+        LOG(ERROR) << "Expect colName size is " << yieldNames_.size()
+                   << ", return colName size is " << data.colNames.size() - 1;
+        return Status::Error("Wrong return prop size");
+    }
+    DataSet result;
+    result.colNames = std::move(yieldNames_);
+    for (auto &row : data.rows) {
+        std::vector<Value> columns;
+        for (auto i = 1u; i < row.values.size(); i++) {
+            columns.emplace_back(std::move(row.values[i]));
+        }
+        result.rows.emplace_back(std::move(columns));
+    }
+    return result;
+}
+
+Status UpdateBaseExecutor::handleErrorCode(nebula::storage::cpp2::ErrorCode code,
+                                           PartitionID partId) {
+    switch (code) {
+        case storage::cpp2::ErrorCode::E_INVALID_FIELD_VALUE:
+            return Status::Error(
+                    "Invalid field value: may be the filed without default value or wrong schema");
+        case storage::cpp2::ErrorCode::E_INVALID_FILTER:
+            return Status::Error("Invalid filter.");
+        case storage::cpp2::ErrorCode::E_INVALID_UPDATER:
+            return Status::Error("Invalid Update col or yield col.");
+        case storage::cpp2::ErrorCode::E_TAG_NOT_FOUND:
+            return Status::Error("Tag `%s' not found.", schemaName_.c_str());
+        case storage::cpp2::ErrorCode::E_TAG_PROP_NOT_FOUND:
+            return Status::Error("Tag prop not found.");
+        case storage::cpp2::ErrorCode::E_EDGE_NOT_FOUND:
+            return Status::Error("Edge `%s' not found.", schemaName_.c_str());
+        case storage::cpp2::ErrorCode::E_EDGE_PROP_NOT_FOUND:
+            return Status::Error("Edge prop not found.");
+        case storage::cpp2::ErrorCode::E_INVALID_DATA:
+            return Status::Error("Invalid data, may be wrong value type.");
+        case storage::cpp2::ErrorCode::E_NOT_NULLABLE:
+            return Status::Error("The not null field cannot be null.");
+        case storage::cpp2::ErrorCode::E_FIELD_UNSET:
+            return Status::Error("The not null field doesn't have a default value.");
+        case storage::cpp2::ErrorCode::E_OUT_OF_RANGE:
+            return Status::Error("Out of range value.");
+        case storage::cpp2::ErrorCode::E_ATOMIC_OP_FAILED:
+            return Status::Error("Atomic operation failed.");
+        case storage::cpp2::ErrorCode::E_FILTER_OUT:
+            return Status::OK();
+        default:
+            auto status = Status::Error("Unknown error, part: %d, error code: %d.",
+                                         partId, static_cast<int32_t>(code));
+            LOG(ERROR) << status;
+            return status;
+    }
+    return Status::OK();
+}
+
+folly::Future<Status> UpdateVertexExecutor::execute() {
+    SCOPED_TIMER(&execTime_);
+    auto *uvNode = asNode<UpdateVertex>(node());
+    yieldNames_ = uvNode->getYieldNames();
+    schemaName_ = uvNode->getName();
+    time::Duration updateVertTime;
+    return qctx()->getStorageClient()->updateVertex(uvNode->getSpaceId(),
+                                                    uvNode->getVId(),
+                                                    uvNode->getTagId(),
+                                                    uvNode->getUpdatedProps(),
+                                                    uvNode->getInsertable(),
+                                                    uvNode->getReturnProps(),
+                                                    uvNode->getCondition())
+        .via(runner())
+        .ensure([updateVertTime]() {
+            VLOG(1) << "Update vertice time: " << updateVertTime.elapsedInUSec() << "us";
+        })
+        .then([this](StatusOr<storage::cpp2::UpdateResponse> resp) {
+            SCOPED_TIMER(&execTime_);
+            if (!resp.ok()) {
+                LOG(ERROR) << resp.status();
+                return resp.status();
+            }
+            auto value = std::move(resp).value();
+            for (auto& code : value.get_result().get_failed_parts()) {
+                NG_RETURN_IF_ERROR(handleErrorCode(code.get_code(), code.get_part_id()));
+            }
+            if (value.__isset.props) {
+                auto status = handleResult(std::move(*value.get_props()));
+                if (!status.ok()) {
+                    return status.status();
+                }
+                return finish(ResultBuilder()
+                                  .value(std::move(status).value())
+                                  .iter(Iterator::Kind::kDefault)
+                                  .finish());
+            }
+            return Status::OK();
+        });
+}
+
+folly::Future<Status> UpdateEdgeExecutor::execute() {
+    SCOPED_TIMER(&execTime_);
+    auto *ueNode = asNode<UpdateEdge>(node());
+    schemaName_ = ueNode->getName();
+    storage::cpp2::EdgeKey edgeKey;
+    edgeKey.set_src(ueNode->getSrcId());
+    edgeKey.set_ranking(ueNode->getRank());
+    edgeKey.set_edge_type(ueNode->getEdgeType());
+    edgeKey.set_dst(ueNode->getDstId());
+    yieldNames_ = ueNode->getYieldNames();
+
+    time::Duration updateEdgeTime;
+    return qctx()->getStorageClient()->updateEdge(ueNode->getSpaceId(),
+                                                  edgeKey,
+                                                  ueNode->getUpdatedProps(),
+                                                  ueNode->getInsertable(),
+                                                  ueNode->getReturnProps(),
+                                                  ueNode->getCondition())
+            .via(runner())
+            .ensure([updateEdgeTime]() {
+                VLOG(1) << "Update edge time: " << updateEdgeTime.elapsedInUSec() << "us";
+            })
+            .then([this](StatusOr<storage::cpp2::UpdateResponse> resp) {
+                SCOPED_TIMER(&execTime_);
+                if (!resp.ok()) {
+                    LOG(ERROR) << "Update edge failed: " << resp.status();
+                    return resp.status();
+                }
+                auto value = std::move(resp).value();
+                for (auto& code : value.get_result().get_failed_parts()) {
+                    NG_RETURN_IF_ERROR(handleErrorCode(code.get_code(), code.get_part_id()));
+                }
+                if (value.__isset.props) {
+                    auto status = handleResult(std::move(*value.get_props()));
+                    if (!status.ok()) {
+                        return status.status();
+                    }
+                    return finish(ResultBuilder()
+                                    .value(std::move(status).value())
+                                    .iter(Iterator::Kind::kDefault)
+                                    .finish());
+                }
+                return Status::OK();
+            });
+}
+}   // namespace graph
+}   // namespace nebula
diff --git a/src/exec/mutate/UpdateExecutor.h b/src/exec/mutate/UpdateExecutor.h
new file mode 100644
index 0000000000000000000000000000000000000000..99bef8cccc9cf6c0ab548f6fb9585337ba398ec7
--- /dev/null
+++ b/src/exec/mutate/UpdateExecutor.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#ifndef EXEC_MUTATE_UPDATEEXECUTOR_H_
+#define EXEC_MUTATE_UPDATEEXECUTOR_H_
+
+#include "common/base/StatusOr.h"
+#include "exec/Executor.h"
+
+namespace nebula {
+namespace graph {
+
+class UpdateBaseExecutor : public Executor {
+public:
+    UpdateBaseExecutor(const std::string &execName,
+                       const PlanNode *node,
+                       QueryContext *ectx)
+        : Executor(execName, node, ectx) {}
+
+    virtual ~UpdateBaseExecutor() {}
+
+protected:
+    StatusOr<DataSet> handleResult(DataSet &&data);
+
+    Status handleErrorCode(nebula::storage::cpp2::ErrorCode code, PartitionID partId);
+
+protected:
+    std::vector<std::string>         yieldNames_;
+    std::string                      schemaName_;
+};
+
+class UpdateVertexExecutor final : public UpdateBaseExecutor {
+public:
+    UpdateVertexExecutor(const PlanNode *node, QueryContext *ectx)
+        : UpdateBaseExecutor("UpdateVertexExecutor", node, ectx) {}
+
+    folly::Future<Status> execute() override;
+};
+
+class UpdateEdgeExecutor final : public UpdateBaseExecutor {
+public:
+    UpdateEdgeExecutor(const PlanNode *node, QueryContext *ectx)
+        : UpdateBaseExecutor("UpdateEdgeExecutor", node, ectx) {}
+
+    folly::Future<Status> execute() override;
+};
+
+}   // namespace graph
+}   // namespace nebula
+
+#endif   // EXEC_MUTATE_UPDATEEXECUTOR_H_
diff --git a/src/mock/CMakeLists.txt b/src/mock/CMakeLists.txt
index 1ab91a0d8ed75c5451c7d484432fc4126862a92c..6fd56e8bd21ba315822c4830cb334c46b34e4e07 100644
--- a/src/mock/CMakeLists.txt
+++ b/src/mock/CMakeLists.txt
@@ -6,13 +6,13 @@
 
 nebula_add_library(
     mock_obj OBJECT
-        MetaCache.cpp
-        StorageCache.cpp
-        MockMetaServiceHandler.cpp
-        MockStorageServiceHandler.cpp
-        test/TestMain.cpp
-        test/TestEnv.cpp
-        test/TestBase.cpp
+    MetaCache.cpp
+    StorageCache.cpp
+    MockMetaServiceHandler.cpp
+    MockStorageServiceHandler.cpp
+    test/TestMain.cpp
+    test/TestEnv.cpp
+    test/TestBase.cpp
 )
 
-nebula_add_subdirectory(test)
\ No newline at end of file
+nebula_add_subdirectory(test)
diff --git a/src/parser/AdminSentences.h b/src/parser/AdminSentences.h
index 1acfbe41b44cee3334da2f8bc2f7103e7291d539..40fc531de92a1c38d1dde3378973bf0d0a2de677 100644
--- a/src/parser/AdminSentences.h
+++ b/src/parser/AdminSentences.h
@@ -130,23 +130,23 @@ public:
         optValue_ = val;
     }
 
-    int64_t asInt() {
+    int64_t asInt() const {
         return boost::get<int64_t>(optValue_);
     }
 
-    const std::string& asString() {
+    const std::string& asString() const {
         return boost::get<std::string>(optValue_);
     }
 
-    bool isInt() {
+    bool isInt() const {
         return optValue_.which() == 0;
     }
 
-    bool isString() {
+    bool isString() const {
         return optValue_.which() == 1;
     }
 
-    int64_t getPartitionNum() {
+    int64_t getPartitionNum() const {
         if (isInt()) {
             return asInt();
         } else {
@@ -155,7 +155,7 @@ public:
         }
     }
 
-    int64_t getReplicaFactor() {
+    int64_t getReplicaFactor() const {
         if (isInt()) {
             return asInt();
         } else {
@@ -164,7 +164,7 @@ public:
         }
     }
 
-    int32_t getVidSize() {
+    int32_t getVidSize() const {
         if (isInt()) {
             return asInt();
         } else {
@@ -173,7 +173,7 @@ public:
         }
     }
 
-    std::string getCharset() {
+    std::string getCharset() const {
         if (isString()) {
             return asString();
         } else {
@@ -182,7 +182,7 @@ public:
         }
     }
 
-    std::string getCollate() {
+    std::string getCollate() const {
         if (isString()) {
             return asString();
         } else {
@@ -191,7 +191,7 @@ public:
         }
     }
 
-    OptionType getOptType() {
+    OptionType getOptType() const {
         return optType_;
     }
 
diff --git a/src/parser/MutateSentences.cpp b/src/parser/MutateSentences.cpp
index 40017d7a640c732c1f94b38cec70540519216c6c..3f5a1b3807121f9727ab01fb31c293a92eda96c0 100644
--- a/src/parser/MutateSentences.cpp
+++ b/src/parser/MutateSentences.cpp
@@ -156,7 +156,12 @@ std::string InsertEdgesSentence::toString() const {
 std::string UpdateItem::toString() const {
     std::string buf;
     buf.reserve(256);
-    buf += *field_;
+    if (fieldStr_ != nullptr)  {
+        buf += *fieldStr_;
+    } else if (fieldExpr_ != nullptr) {
+        buf += fieldExpr_->toString();
+    }
+
     buf += "=";
     buf += value_->toString();
     return buf;
@@ -196,7 +201,6 @@ StatusOr<std::string> UpdateList::toEvaledString() const {
     return buf;
 }
 
-
 std::string UpdateVertexSentence::toString() const {
     std::string buf;
     buf.reserve(256);
@@ -206,6 +210,7 @@ std::string UpdateVertexSentence::toString() const {
         buf += "UPDATE ";
     }
     buf += "VERTEX ";
+    buf += "ON " + *name_ + " ";
     buf += vid_->toString();
     buf += " SET ";
     buf += updateList_->toString();
@@ -231,13 +236,11 @@ std::string UpdateEdgeSentence::toString() const {
         buf += "UPDATE ";
     }
     buf += "EDGE ";
-    buf += srcid_->toString();
+    buf += srcId_->toString();
     buf += "->";
-    buf += dstid_->toString();
-    if (hasRank_) {
-        buf += " AT" + std::to_string(rank_);
-    }
-    buf += " OF " + *edgeType_;
+    buf += dstId_->toString();
+    buf += " AT" + std::to_string(rank_);
+    buf += " OF " + *name_;
     buf += " SET ";
     buf += updateList_->toString();
     if (whenClause_ != nullptr) {
diff --git a/src/parser/MutateSentences.h b/src/parser/MutateSentences.h
index c9eb9db56cb8ea85f8f3c0ddaf85f10e1d6d9aab..949412f12772224f564f27f79fbc353cd5bc2fe3 100644
--- a/src/parser/MutateSentences.h
+++ b/src/parser/MutateSentences.h
@@ -303,22 +303,24 @@ private:
 class UpdateItem final {
 public:
     UpdateItem(std::string *field, Expression *value) {
-        field_.reset(field);
+        fieldStr_.reset(field);
         value_.reset(value);
     }
 
     UpdateItem(Expression *field, Expression *value) {
-        // TODO
-        UNUSED(field);
-        field_ = std::make_unique<std::string>("");
+        fieldExpr_.reset(field);
         value_.reset(value);
     }
 
-    std::string* field() const {
-        return field_.get();
+    std::string* getFieldName() const {
+        return fieldStr_.get();
     }
 
-    Expression* value() const {
+    const Expression* getFieldExpr() const {
+        return fieldExpr_.get();
+    }
+
+    const Expression* value() const {
         return value_.get();
     }
 
@@ -327,13 +329,17 @@ public:
     StatusOr<std::string> toEvaledString() const;
 
 private:
-    std::unique_ptr<std::string>                field_;
+    std::unique_ptr<std::string>                fieldStr_;
+    std::unique_ptr<Expression>                 fieldExpr_;
     std::unique_ptr<Expression>                 value_;
 };
 
 
 class UpdateList final {
 public:
+    UpdateList() = default;
+    ~UpdateList() = default;
+
     void addItem(UpdateItem *item) {
         items_.emplace_back(item);
     }
@@ -355,147 +361,137 @@ private:
     std::vector<std::unique_ptr<UpdateItem>>    items_;
 };
 
-
-class UpdateVertexSentence final : public Sentence {
+class UpdateBaseSentence : public Sentence {
 public:
-    UpdateVertexSentence() {
-        kind_ = Kind::kUpdateVertex;
+    UpdateBaseSentence(UpdateList *updateList,
+                       WhenClause *whenClause,
+                       YieldClause *yieldClause,
+                       std::string* name,
+                       bool isInsertable = false) {
+        updateList_.reset(updateList);
+        whenClause_.reset(whenClause);
+        yieldClause_.reset(yieldClause);
+        name_.reset(name);
+        insertable_ = isInsertable;
     }
 
-    void setInsertable(bool insertable) {
-        insertable_ = insertable;
-    }
+    virtual ~UpdateBaseSentence() = default;
 
     bool getInsertable() const {
         return insertable_;
     }
 
-    void setVid(Expression *vid) {
-        vid_.reset(vid);
-    }
-
-    Expression* getVid() const {
-        return vid_.get();
-    }
-
-    void setUpdateList(UpdateList *updateList) {
-        updateList_.reset(updateList);
-    }
-
     const UpdateList* updateList() const {
         return updateList_.get();
     }
 
-    void setWhenClause(WhenClause *clause) {
-        whenClause_.reset(clause);
-    }
-
     const WhenClause* whenClause() const {
         return whenClause_.get();
     }
 
-    void setYieldClause(YieldClause *clause) {
-        yieldClause_.reset(clause);
-    }
-
     const YieldClause* yieldClause() const {
         return yieldClause_.get();
     }
 
-    std::string toString() const override;
+    const std::string* getName() const {
+        return name_.get();
+    }
 
-private:
+protected:
     bool                                        insertable_{false};
-    std::unique_ptr<Expression>                 vid_;
     std::unique_ptr<UpdateList>                 updateList_;
     std::unique_ptr<WhenClause>                 whenClause_;
     std::unique_ptr<YieldClause>                yieldClause_;
+    std::unique_ptr<std::string>                name_;
 };
 
-
-class UpdateEdgeSentence final : public Sentence {
+class UpdateVertexSentence final : public UpdateBaseSentence {
 public:
-    UpdateEdgeSentence() {
-        kind_ = Kind::kUpdateEdge;
+    UpdateVertexSentence(Expression *vid,
+                         std::string *tagName,
+                         UpdateList *updateList,
+                         WhenClause *whenClause,
+                         YieldClause *yieldClause,
+                         bool isInsertable = false)
+        : UpdateBaseSentence(updateList, whenClause, yieldClause, tagName, isInsertable) {
+        kind_ = Kind::kUpdateVertex;
+        vid_.reset(vid);
     }
 
-    void setInsertable(bool insertable) {
-        insertable_ = insertable;
+    UpdateVertexSentence(Expression *vid,
+                         UpdateList *updateList,
+                         WhenClause *whenClause,
+                         YieldClause *yieldClause,
+                         bool isInsertable = false)
+        : UpdateBaseSentence(updateList, whenClause, yieldClause, nullptr, isInsertable) {
+        kind_ = Kind::kUpdateVertex;
+        vid_.reset(vid);
     }
 
+    ~UpdateVertexSentence() {}
+
     bool getInsertable() const {
         return insertable_;
     }
 
-    void setSrcId(Expression* srcid) {
-        srcid_.reset(srcid);
-    }
-
-    Expression* getSrcId() const {
-        return srcid_.get();
-    }
-
-    void setDstId(Expression* dstid) {
-        dstid_.reset(dstid);
-    }
-
-    Expression* getDstId() const {
-        return dstid_.get();
+    Expression* getVid() const {
+        return vid_.get();
     }
 
-    void setRank(int64_t rank) {
-        rank_ = rank;
-        hasRank_ = true;
+    const UpdateList* updateList() const {
+        return updateList_.get();
     }
 
-    int64_t getRank() const {
-        return rank_;
+    const WhenClause* whenClause() const {
+        return whenClause_.get();
     }
 
-    void setEdgeType(std::string* edgeType) {
-        edgeType_.reset(edgeType);
+    const YieldClause* yieldClause() const {
+        return yieldClause_.get();
     }
 
-    const std::string* getEdgeType() const {
-        return edgeType_.get();
-    }
+    std::string toString() const override;
 
-    void setUpdateList(UpdateList *updateList) {
-        updateList_.reset(updateList);
-    }
+private:
+    std::unique_ptr<Expression>                 vid_;
+};
 
-    const UpdateList* updateList() const {
-        return updateList_.get();
-    }
 
-    void setWhenClause(WhenClause *clause) {
-        whenClause_.reset(clause);
+class UpdateEdgeSentence final : public UpdateBaseSentence {
+public:
+    UpdateEdgeSentence(Expression *srcId,
+                       Expression *dstId,
+                       int64_t rank,
+                       std::string *edgeName,
+                       UpdateList *updateList,
+                       WhenClause *whenClause,
+                       YieldClause *yieldClause,
+                       bool isInsertable = false)
+        : UpdateBaseSentence(updateList, whenClause, yieldClause, edgeName, isInsertable) {
+        kind_ = Kind::kUpdateEdge;
+        srcId_.reset(srcId);
+        dstId_.reset(dstId);
+        rank_ = rank;
     }
 
-    const WhenClause* whenClause() const {
-        return whenClause_.get();
+    Expression* getSrcId() const {
+        return srcId_.get();
     }
 
-    void setYieldClause(YieldClause *clause) {
-        yieldClause_.reset(clause);
+    Expression* getDstId() const {
+        return dstId_.get();
     }
 
-    const YieldClause* yieldClause() const {
-        return yieldClause_.get();
+    int64_t getRank() const {
+        return rank_;
     }
 
     std::string toString() const override;
 
 private:
-    bool                                        insertable_{false};
-    bool                                        hasRank_{false};
-    std::unique_ptr<Expression>                 srcid_;
-    std::unique_ptr<Expression>                 dstid_;
+    std::unique_ptr<Expression>                 srcId_;
+    std::unique_ptr<Expression>                 dstId_;
     int64_t                                     rank_{0L};
-    std::unique_ptr<std::string>                edgeType_;
-    std::unique_ptr<UpdateList>                 updateList_;
-    std::unique_ptr<WhenClause>                 whenClause_;
-    std::unique_ptr<YieldClause>                yieldClause_;
 };
 
 
diff --git a/src/parser/parser.yy b/src/parser/parser.yy
index 99f8ca2d09f96e5796b47a1e23182860852d4156..156b494868a4faed4b2a7ba93158c37407231b26 100644
--- a/src/parser/parser.yy
+++ b/src/parser/parser.yy
@@ -390,6 +390,10 @@ base_expression
     | function_call_expression {
         $$ = $1;
     }
+    | name_label {
+        // need to rewrite the expression
+        $$ = new SymbolPropertyExpression(Expression::Kind::kSymProperty, new std::string(""), new std::string(""), $1);
+    }
     ;
 
 input_ref_expression
@@ -1492,26 +1496,6 @@ edge_row_item
 
 rank: unary_integer { $$ = $1; };
 
-update_vertex_sentence
-    : KW_UPDATE KW_VERTEX vid KW_SET update_list when_clause yield_clause {
-        auto sentence = new UpdateVertexSentence();
-        sentence->setVid($3);
-        sentence->setUpdateList($5);
-        sentence->setWhenClause($6);
-        sentence->setYieldClause($7);
-        $$ = sentence;
-    }
-    | KW_UPSERT KW_VERTEX vid KW_SET update_list when_clause yield_clause {
-        auto sentence = new UpdateVertexSentence();
-        sentence->setInsertable(true);
-        sentence->setVid($3);
-        sentence->setUpdateList($5);
-        sentence->setWhenClause($6);
-        sentence->setYieldClause($7);
-        $$ = sentence;
-    }
-    ;
-
 update_list
     : update_item {
         $$ = new UpdateList();
@@ -1527,59 +1511,74 @@ update_item
     : name_label ASSIGN expression {
         $$ = new UpdateItem($1, $3);
     }
-    | alias_ref_expression ASSIGN expression {
-        $$ = new UpdateItem($1, $3);
-        delete $1;
+    | name_label DOT name_label ASSIGN expression {
+        auto symExpr = new SymbolPropertyExpression(Expression::Kind::kSymProperty, new std::string(""), $1, $3);
+        $$ = new UpdateItem(symExpr, $5);
+    }
+    ;
+
+update_vertex_sentence
+    // ======== Begin: Compatible with 1.0 =========
+    : KW_UPDATE KW_VERTEX vid KW_SET update_list when_clause yield_clause {
+        auto sentence = new UpdateVertexSentence($3, $5, $6, $7);
+        $$ = sentence;
+    }
+    | KW_UPSERT KW_VERTEX vid KW_SET update_list when_clause yield_clause {
+        auto sentence = new UpdateVertexSentence($3, $5, $6, $7,true);
+        $$ = sentence;
+    }
+     // ======== End: Compatible with 1.0 =========
+    | KW_UPDATE KW_VERTEX KW_ON name_label vid KW_SET update_list when_clause yield_clause {
+        auto sentence = new UpdateVertexSentence($5, $4, $7, $8, $9);
+        $$ = sentence;
+    }
+    | KW_UPSERT KW_VERTEX KW_ON name_label vid KW_SET update_list when_clause yield_clause {
+        auto sentence = new UpdateVertexSentence($5, $4, $7, $8, $9, true);
+        $$ = sentence;
     }
     ;
 
 update_edge_sentence
+    // ======== Begin: Compatible with 1.0 =========
     : KW_UPDATE KW_EDGE vid R_ARROW vid KW_OF name_label
       KW_SET update_list when_clause yield_clause {
-        auto sentence = new UpdateEdgeSentence();
-        sentence->setSrcId($3);
-        sentence->setDstId($5);
-        sentence->setEdgeType($7);
-        sentence->setUpdateList($9);
-        sentence->setWhenClause($10);
-        sentence->setYieldClause($11);
+        auto sentence = new UpdateEdgeSentence($3, $5, 0, $7, $9, $10, $11);
         $$ = sentence;
     }
     | KW_UPSERT KW_EDGE vid R_ARROW vid KW_OF name_label
       KW_SET update_list when_clause yield_clause {
-        auto sentence = new UpdateEdgeSentence();
-        sentence->setInsertable(true);
-        sentence->setSrcId($3);
-        sentence->setDstId($5);
-        sentence->setEdgeType($7);
-        sentence->setUpdateList($9);
-        sentence->setWhenClause($10);
-        sentence->setYieldClause($11);
+        auto sentence = new UpdateEdgeSentence($3, $5, 0, $7, $9, $10, $11, true);
         $$ = sentence;
     }
     | KW_UPDATE KW_EDGE vid R_ARROW vid AT rank KW_OF name_label
       KW_SET update_list when_clause yield_clause {
-        auto sentence = new UpdateEdgeSentence();
-        sentence->setSrcId($3);
-        sentence->setDstId($5);
-        sentence->setRank($7);
-        sentence->setEdgeType($9);
-        sentence->setUpdateList($11);
-        sentence->setWhenClause($12);
-        sentence->setYieldClause($13);
+        auto sentence = new UpdateEdgeSentence($3, $5, $7, $9, $11, $12, $13);
         $$ = sentence;
     }
     | KW_UPSERT KW_EDGE vid R_ARROW vid AT rank KW_OF name_label
       KW_SET update_list when_clause yield_clause {
-        auto sentence = new UpdateEdgeSentence();
-        sentence->setInsertable(true);
-        sentence->setSrcId($3);
-        sentence->setDstId($5);
-        sentence->setRank($7);
-        sentence->setEdgeType($9);
-        sentence->setUpdateList($11);
-        sentence->setWhenClause($12);
-        sentence->setYieldClause($13);
+        auto sentence = new UpdateEdgeSentence($3, $5, $7, $9, $11, $12, $13, true);
+        $$ = sentence;
+    }
+    // ======== End: Compatible with 1.0 =========
+    | KW_UPDATE KW_EDGE KW_ON name_label vid R_ARROW vid
+      KW_SET update_list when_clause yield_clause {
+        auto sentence = new UpdateEdgeSentence($5, $7, 0, $4, $9, $10, $11);
+        $$ = sentence;
+    }
+    | KW_UPSERT KW_EDGE KW_ON name_label vid R_ARROW vid
+      KW_SET update_list when_clause yield_clause {
+        auto sentence = new UpdateEdgeSentence($5, $7, 0, $4, $9, $10, $11, true);
+        $$ = sentence;
+    }
+    | KW_UPDATE KW_EDGE KW_ON name_label vid R_ARROW vid AT rank
+      KW_SET update_list when_clause yield_clause {
+        auto sentence = new UpdateEdgeSentence($5, $7, $9, $4, $11, $12, $13);
+        $$ = sentence;
+    }
+    | KW_UPSERT KW_EDGE KW_ON name_label vid R_ARROW vid AT rank
+      KW_SET update_list when_clause yield_clause {
+        auto sentence = new UpdateEdgeSentence($5, $7, $9, $4, $11, $12, $13, true);
         $$ = sentence;
     }
     ;
diff --git a/src/parser/test/ParserTest.cpp b/src/parser/test/ParserTest.cpp
index 41e62be55b94907da16350b10f5a1e305fb2ff1a..4b71a58d495a8a45ea23879d08a262fef1e3750a 100644
--- a/src/parser/test/ParserTest.cpp
+++ b/src/parser/test/ParserTest.cpp
@@ -145,7 +145,7 @@ TEST(Parser, Go) {
     }
     {
         GQLParser parser;
-        std::string query = "GO FROM \"1\",\"2\",\"3\" OVER friend WHERE person.name == \"dutor\"";
+        std::string query = "GO FROM \"1\",\"2\",\"3\" OVER friend WHERE person.name == \"Tom\"";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -643,7 +643,7 @@ TEST(Parser, InsertVertex) {
     {
         GQLParser parser;
         std::string query = "INSERT VERTEX person(name,age,married,salary,create_time) "
-                            "VALUES \"dutor\":(\"dutor\", 30, true, 3.14, 1551331900)";
+                            "VALUES \"Tom\":(\"Tom\", 30, true, 3.14, 1551331900)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -679,21 +679,21 @@ TEST(Parser, InsertVertex) {
     {
         GQLParser parser;
         std::string query = "INSERT VERTEX person(name,age,married,salary,create_time) "
-                            "VALUES \"dutor\":(\"dutor\", 30, true, 3.14, 1551331900)";
+                            "VALUES \"Tom\":(\"Tom\", 30, true, 3.14, 1551331900)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
         std::string query = "INSERT VERTEX person(name,age,married,salary,create_time) "
-                            "VALUES \"dutor\":(\"dutor\", 30, true, 3.14, 1551331900)";
+                            "VALUES \"Tom\":(\"Tom\", 30, true, 3.14, 1551331900)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
         std::string query = "INSERT VERTEX person(name,age,married,salary,create_time) "
-                            "VALUES \"dutor\":(\"dutor\", 30, true, 3.14, 1551331900)";
+                            "VALUES \"Tom\":(\"Tom\", 30, true, 3.14, 1551331900)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -709,7 +709,7 @@ TEST(Parser, InsertVertex) {
     {
         GQLParser parser;
         std::string query = "INSERT VERTEX person(name, age) "
-                            "VALUES \"dutor\":(\"dutor, 30)";
+                            "VALUES \"Tom\":(\"Tom, 30)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.status().isSyntaxError());
     }
@@ -717,21 +717,21 @@ TEST(Parser, InsertVertex) {
     {
         GQLParser parser;
         std::string query = "INSERT VERTEX person(name, age) "
-                            "VALUES \"dutor\":(\'dutor, 30)";
+                            "VALUES \"Tom\":(\'Tom, 30)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.status().isSyntaxError());
     }
     {
         GQLParser parser;
         std::string query = "INSERT VERTEX person(name, age) "
-                            "VALUES \"dutor\":(\'dutor, 30)";
+                            "VALUES \"Tom\":(\'Tom, 30)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.status().isSyntaxError());
     }
     {
         GQLParser parser;
         std::string query = "INSERT VERTEX person(name, age) "
-                            "VALUES \"dutor\":(\'dutor, 30)";
+                            "VALUES \"Tom\":(\'Tom, 30)";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.status().isSyntaxError());
     }
@@ -740,49 +740,91 @@ TEST(Parser, InsertVertex) {
 TEST(Parser, UpdateVertex) {
     {
         GQLParser parser;
-        std::string query = "UPDATE VERTEX \"dutor\" "
-                            "SET person.name=\"dutor\", person.age=30, "
-                                "job.salary=10000, person.create_time=1551331999";
+        std::string query = "UPDATE VERTEX \"12345\" "
+                            "SET person.name=\"Tome\", person.age=30, "
+                            "job.salary=10000, person.create_time=1551331999";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "UPDATE VERTEX \"dutor\" "
-                            "SET person.name=\"dutor\", person.age=$^.person.age + 1, "
-                                "person.married=true "
+        std::string query = "UPDATE VERTEX ON person \"12345\" "
+                            "SET name=\"Tome\", age=30";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "UPDATE VERTEX \"12345\" "
+                            "SET person.name=\"Tom\", person.age=$^.person.age + 1, "
+                            "person.married=true "
                             "WHEN $^.job.salary > 10000 && $^.person.age > 30";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "UPDATE VERTEX \"dutor\" "
-                            "SET person.name=\"dutor\", person.age=31, person.married=true, "
-                                "job.salary=1.1 * $^.person.create_time / 31536000 "
+        std::string query = "UPDATE VERTEX \"12345\" "
+                            "SET name=\"Tom\", age=age + 1, "
+                            "married=true "
+                            "WHEN salary > 10000 && age > 30";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "UPDATE VERTEX \"12345\" "
+                            "SET person.name=\"Tom\", person.age=31, person.married=true, "
+                            "job.salary=1.1 * $^.person.create_time / 31536000 "
                             "YIELD $^.person.name AS Name, job.name AS Title, "
-                                  "$^.job.salary AS Salary";
+                            "$^.job.salary AS Salary";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "UPDATE VERTEX ON person \"12345\" "
+                            "SET name=\"Tom\", age=31, married=true "
+                            "YIELD name AS Name";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "UPDATE VERTEX \"dutor\" "
-                            "SET person.name=\"dutor\", person.age=30, person.married=true "
+        std::string query = "UPDATE VERTEX \"12345\" "
+                            "SET person.name=\"Tom\", person.age=30, person.married=true "
                             "WHEN $^.job.salary > 10000 && $^.job.name == \"CTO\" || "
-                                  "$^.person.age < 30"
+                            "$^.person.age < 30"
                             "YIELD $^.person.name AS Name, $^.job.salary AS Salary, "
-                                  "$^.person.create_time AS Time";
+                            "$^.person.create_time AS Time";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "UPSERT VERTEX \"dutor\" "
-                            "SET person.name=\"dutor\", person.age = 30, job.name =\"CTO\" "
+        std::string query = "UPDATE VERTEX ON person \"12345\" "
+                            "SET name=\"Tom\", age=30, married=true "
+                            "WHEN salary > 10000 && name == \"CTO\" || age < 30"
+                            "YIELD name AS Name, salary AS Salary, create_time AS Time";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "UPSERT VERTEX \"12345\" "
+                            "SET person.name=\"Tom\", person.age = 30, job.name =\"CTO\" "
                             "WHEN $^.job.salary > 10000 "
                             "YIELD $^.person.name AS Name, $^.job.salary AS Salary, "
-                                  "$^.person.create_time AS Time";
+                            "$^.person.create_time AS Time";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "UPSERT VERTEX ON person \"12345\" "
+                            "SET name=\"Tom\", age = 30, name =\"CTO\" "
+                            "WHEN salary > 10000 "
+                            "YIELD name AS Name, salary AS Salary, create_time AS Time";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -866,11 +908,26 @@ TEST(Parser, UpdateEdge) {
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
+    {
+        GQLParser parser;
+        std::string query = "UPDATE EDGE ON transfer \"12345\" -> \"54321\" "
+                            "SET amount=3.14, time=1537408527";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
     {
         GQLParser parser;
         std::string query = "UPDATE EDGE \"12345\" -> \"54321\"@789 OF transfer "
                             "SET amount=3.14,time=1537408527 "
-                            "WHEN transfer.amount > 3.14 && $^.person.name == \"dutor\"";
+                            "WHEN transfer.amount > 3.14 && $^.person.name == \"Tom\"";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "UPDATE EDGE ON transfer \"12345\" -> \"54321\"@789 "
+                            "SET amount=3.14,time=1537408527 "
+                            "WHEN amount > 3.14";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -880,7 +937,16 @@ TEST(Parser, UpdateEdge) {
                             "SET amount = 3.14 + $^.job.salary, time = 1537408527 "
                             "WHEN transfer.amount > 3.14 || $^.job.salary >= 10000 "
                             "YIELD transfer.amount, transfer.time AS Time, "
-                                "$^.person.name AS PayFrom";
+                            "$^.person.name AS PayFrom";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "UPDATE EDGE ON transfer \"12345\" -> \"54321\" "
+                            "SET amount = 3.14 + amount, time = 1537408527 "
+                            "WHEN amount > 3.14 "
+                            "YIELD amount, time AS Time";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
@@ -893,6 +959,15 @@ TEST(Parser, UpdateEdge) {
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
+    {
+        GQLParser parser;
+        std::string query = "UPSERT EDGE ON transfer \"12345\" -> \"54321\" @789 "
+                            "SET amount=$^.job.salary + 3.14, time=1537408527 "
+                            "WHEN amount > 3.14 && salary >= 10000 "
+                            "YIELD amount, time, name AS Name";
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
 }
 
 TEST(Parser, DeleteVertex) {
@@ -988,26 +1063,26 @@ TEST(Parser, FetchVertex) {
     }
     {
         GQLParser parser;
-        std::string query = "FETCH PROP ON person \"dutor\"";
+        std::string query = "FETCH PROP ON person \"Tom\"";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "FETCH PROP ON person \"dutor\", \"darion\"";
+        std::string query = "FETCH PROP ON person \"Tom\", \"darion\"";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "FETCH PROP ON person \"dutor\" "
+        std::string query = "FETCH PROP ON person \"Tom\" "
                             "YIELD person.name, person.age";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "FETCH PROP ON person \"dutor\", \"darion\" "
+        std::string query = "FETCH PROP ON person \"Tom\", \"darion\" "
                             "YIELD person.name, person.age";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
@@ -1035,26 +1110,26 @@ TEST(Parser, FetchVertex) {
     }
     {
         GQLParser parser;
-        std::string query = "FETCH PROP ON person uuid(\"dutor\")";
+        std::string query = "FETCH PROP ON person uuid(\"Tom\")";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "FETCH PROP ON person \"dutor\", \"darion\"";
+        std::string query = "FETCH PROP ON person \"Tom\", \"darion\"";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "FETCH PROP ON person \"dutor\" "
+        std::string query = "FETCH PROP ON person \"Tom\" "
                             "YIELD person.name, person.age";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
     }
     {
         GQLParser parser;
-        std::string query = "FETCH PROP ON person \"dutor\", \"darion\" "
+        std::string query = "FETCH PROP ON person \"Tom\", \"darion\" "
                             "YIELD person.name, person.age";
         auto result = parser.parse(query);
         ASSERT_TRUE(result.ok()) << result.status();
@@ -2037,22 +2112,6 @@ TEST(Parser, UseReservedKeyword) {
     }
 }
 
-TEST(Parser, IssueLabelAsExpression) {
-    // name label is not a valid expression, it's not value
-    {
-        GQLParser parser;
-        std::string query = "INSERT VERTEX person(name) VALUES \"1\":(name_label)";
-        auto result = parser.parse(query);
-        ASSERT_FALSE(result.ok());
-    }
-    {
-        GQLParser parser;
-        std::string query = "INSERT VERTEX person(name) VALUES \"1\":(`name_label`)";
-        auto result = parser.parse(query);
-        ASSERT_FALSE(result.ok());
-    }
-}
-
 TEST(Parser, TypeCast) {
     {
         GQLParser parser;
diff --git a/src/planner/Mutate.h b/src/planner/Mutate.h
index f88dc76125c19ad9a30f81c2cb049b420a0598f0..2de152ea1a3ae58becea9941aff67bf483545858 100644
--- a/src/planner/Mutate.h
+++ b/src/planner/Mutate.h
@@ -132,18 +132,223 @@ private:
     bool                                       overwritable_;
 };
 
-class UpdateVertex final : public SingleInputNode {
+class Update : public SingleInputNode {
 public:
+    bool getInsertable() const {
+        return insertable_;
+    }
+
+    const std::vector<std::string>& getReturnProps() const {
+        return returnProps_;
+    }
+
+    const std::string getCondition() const {
+        return condition_;
+    }
+
+    const std::vector<std::string>& getYieldNames() const {
+        return yieldNames_;
+    }
+
+    GraphSpaceID getSpaceId() const {
+        return spaceId_;
+    }
+
+    const std::vector<storage::cpp2::UpdatedProp>& getUpdatedProps() const {
+        return updatedProps_;
+    }
+
+    const std::string& getName() const {
+        return schemaName_;
+    }
+
+protected:
+    Update(Kind kind,
+           ExecutionPlan* plan,
+           PlanNode* input,
+           GraphSpaceID spaceId,
+           std::string name,
+           bool insertable,
+           std::vector<storage::cpp2::UpdatedProp> updatedProps,
+           std::vector<std::string> returnProps,
+           std::string condition,
+           std::vector<std::string> yieldNames)
+        : SingleInputNode(plan, kind, input)
+        , spaceId_(spaceId)
+        , schemaName_(std::move(name))
+        , insertable_(insertable)
+        , updatedProps_(std::move(updatedProps))
+        , returnProps_(std::move(returnProps))
+        , condition_(std::move(condition))
+        , yieldNames_(std::move(yieldNames)) {}
+
+protected:
+    GraphSpaceID                                        spaceId_{-1};
+    std::string                                         schemaName_;
+    bool                                                insertable_;
+    std::vector<storage::cpp2::UpdatedProp>             updatedProps_;
+    std::vector<std::string>                            returnProps_;
+    std::string                                         condition_;
+    std::vector<std::string>                            yieldNames_;
+};
+
+class UpdateVertex final : public Update {
+public:
+    static UpdateVertex* make(ExecutionPlan* plan,
+                              PlanNode* input,
+                              GraphSpaceID spaceId,
+                              std::string name,
+                              std::string vId,
+                              TagID tagId,
+                              bool insertable,
+                              std::vector<storage::cpp2::UpdatedProp> updatedProps,
+                              std::vector<std::string> returnProps,
+                              std::string condition,
+                              std::vector<std::string> yieldNames) {
+        return new UpdateVertex(plan,
+                                input,
+                                spaceId,
+                                std::move(name),
+                                std::move(vId),
+                                tagId,
+                                insertable,
+                                std::move(updatedProps),
+                                std::move(returnProps),
+                                std::move(condition),
+                                std::move(yieldNames));
+    }
+
     std::string explain() const override {
         return "UpdateVertex";
     }
+
+    const std::string& getVId() const {
+        return vId_;
+    }
+
+    TagID getTagId() const {
+        return tagId_;
+    }
+
+private:
+    UpdateVertex(ExecutionPlan* plan,
+                 PlanNode* input,
+                 GraphSpaceID spaceId,
+                 std::string name,
+                 std::string vId,
+                 TagID tagId,
+                 bool insertable,
+                 std::vector<storage::cpp2::UpdatedProp> updatedProps,
+                 std::vector<std::string> returnProps,
+                 std::string condition,
+                 std::vector<std::string> yieldNames)
+        : Update(Kind::kUpdateVertex,
+                 plan,
+                 input,
+                 spaceId,
+                 std::move(name),
+                 insertable,
+                 std::move(updatedProps),
+                 std::move(returnProps),
+                 std::move(condition),
+                 std::move(yieldNames))
+        , vId_(std::move(vId))
+        , tagId_(tagId) {}
+
+private:
+    std::string                                     vId_;
+    TagID                                           tagId_{-1};
 };
 
-class UpdateEdge final : public SingleInputNode {
+class UpdateEdge final : public Update {
 public:
+    static UpdateEdge* make(ExecutionPlan* plan,
+                            PlanNode* input,
+                            GraphSpaceID spaceId,
+                            std::string name,
+                            std::string srcId,
+                            std::string dstId,
+                            EdgeType edgeType,
+                            int64_t rank,
+                            bool insertable,
+                            std::vector<storage::cpp2::UpdatedProp> updatedProps,
+                            std::vector<std::string> returnProps,
+                            std::string condition,
+                            std::vector<std::string> yieldNames) {
+        return new UpdateEdge(plan,
+                              input,
+                              spaceId,
+                              std::move(name),
+                              std::move(srcId),
+                              std::move(dstId),
+                              edgeType,
+                              rank,
+                              insertable,
+                              std::move(updatedProps),
+                              std::move(returnProps),
+                              std::move(condition),
+                              std::move(yieldNames));
+    }
+
     std::string explain() const override {
         return "UpdateEdge";
     }
+
+    const std::string& getSrcId() const {
+        return srcId_;
+    }
+
+    const std::string& getDstId() const {
+        return dstId_;
+    }
+
+    int64_t getRank() const {
+        return rank_;
+    }
+
+    int64_t getEdgeType() const {
+        return edgeType_;
+    }
+
+    const std::vector<storage::cpp2::UpdatedProp>& getUpdatedProps() const {
+        return updatedProps_;
+    }
+
+private:
+    UpdateEdge(ExecutionPlan* plan,
+               PlanNode* input,
+               GraphSpaceID spaceId,
+               std::string name,
+               std::string srcId,
+               std::string dstId,
+               EdgeType edgeType,
+               int64_t rank,
+               bool insertable,
+               std::vector<storage::cpp2::UpdatedProp> updatedProps,
+               std::vector<std::string> returnProps,
+               std::string condition,
+               std::vector<std::string> yieldNames)
+        : Update(Kind::kUpdateEdge,
+                 plan,
+                 input,
+                 spaceId,
+                 std::move(name),
+                 insertable,
+                 std::move(updatedProps),
+                 std::move(returnProps),
+                 std::move(condition),
+                 std::move(yieldNames))
+
+        , srcId_(std::move(srcId))
+        , dstId_(std::move(dstId))
+        , rank_(rank)
+        , edgeType_(edgeType) {}
+
+private:
+    std::string                                         srcId_;
+    std::string                                         dstId_;
+    int64_t                                             rank_{0};
+    EdgeType                                            edgeType_{-1};
 };
 
 class DeleteVertices final : public SingleInputNode {
diff --git a/src/planner/PlanNode.cpp b/src/planner/PlanNode.cpp
index 6c05e13cf1210627c9b28098193e25447e05d015..f7319b26ddf602b24107064c7dc9c8db8093bb37 100644
--- a/src/planner/PlanNode.cpp
+++ b/src/planner/PlanNode.cpp
@@ -108,6 +108,10 @@ const char* PlanNode::toString(PlanNode::Kind kind) {
             return "DeleteVertices";
         case Kind::kDeleteEdges:
             return "DeleteEdges";
+        case Kind::kUpdateVertex:
+            return "UpdateVertex";
+        case Kind::kUpdateEdge:
+            return "UpdateEdge";
     }
     LOG(FATAL) << "Impossible kind plan node " << static_cast<int>(kind);
 }
diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h
index ea70cf3a357d375eacf97c9c2b2144f753d74150..b4e6241e5f2624062f4110045c06bb40fa2e58c9 100644
--- a/src/planner/PlanNode.h
+++ b/src/planner/PlanNode.h
@@ -68,6 +68,8 @@ public:
         kDataJoin,
         kDeleteVertices,
         kDeleteEdges,
+        kUpdateVertex,
+        kUpdateEdge,
     };
 
     PlanNode(ExecutionPlan* plan, Kind kind);
diff --git a/src/validator/MutateValidator.cpp b/src/validator/MutateValidator.cpp
index 559e68580a50d725da9eb6b93e0c7d056a6daec1..236442200183b6c667745297835be4d40289da4e 100644
--- a/src/validator/MutateValidator.cpp
+++ b/src/validator/MutateValidator.cpp
@@ -88,13 +88,25 @@ Status InsertVerticesValidator::prepareVertices() {
     for (auto i = 0u; i < rows_.size(); i++) {
         auto *row = rows_[i];
         if (propSize_ != row->values().size()) {
-            return Status::Error("Wrong number of value");
+            return Status::Error("Column count doesn't match value count.");
+        }
+        if (!evaluableExpr(row->id())) {
+            LOG(ERROR) << "Wrong vid expression `" << row->id()->toString() << "\"";
+            return Status::Error("Wrong vid expression `%s'", row->id()->toString().c_str());
         }
         auto idStatus = SchemaUtil::toVertexID(row->id());
         if (!idStatus.ok()) {
             return idStatus.status();
         }
         auto vertexId = std::move(idStatus).value();
+
+        // check value expr
+        for (auto &value : row->values()) {
+            if (!evaluableExpr(value)) {
+                LOG(ERROR) << "Insert wrong value: `" << value->toString() << "'.";
+                return Status::Error("Insert wrong value: `%s'.", value->toString().c_str());
+            }
+        }
         auto valsRet = SchemaUtil::toValueVec(row->values());
         if (!valsRet.ok()) {
             return valsRet.status();
@@ -195,8 +207,18 @@ Status InsertEdgesValidator::prepareEdges() {;
     for (auto i = 0u; i < rows_.size(); i++) {
         auto *row = rows_[i];
         if (propNames_.size() != row->values().size()) {
-            return Status::Error("Wrong number of value");
+            return Status::Error("Column count doesn't match value count.");
+        }
+        if (!evaluableExpr(row->srcid())) {
+            LOG(ERROR) << "Wrong src vid expression `" << row->srcid()->toString() << "\"";
+            return Status::Error("Wrong src vid expression `%s'", row->srcid()->toString().c_str());
+        }
+
+        if (!evaluableExpr(row->dstid())) {
+            LOG(ERROR) << "Wrong dst vid expression `" << row->dstid()->toString() << "\"";
+            return Status::Error("Wrong dst vid expression `%s'", row->dstid()->toString().c_str());
         }
+
         auto idStatus = SchemaUtil::toVertexID(row->srcid());
         if (!idStatus.ok()) {
             return idStatus.status();
@@ -210,6 +232,14 @@ Status InsertEdgesValidator::prepareEdges() {;
 
         int64_t rank = row->rank();
 
+        // check value expr
+        for (auto &value : row->values()) {
+            if (!evaluableExpr(value)) {
+                LOG(ERROR) << "Insert wrong value: `" << value->toString() << "'.";
+                return Status::Error("Insert wrong value: `%s'.", value->toString().c_str());
+            }
+        }
+
         auto valsRet = SchemaUtil::toValueVec(row->values());
         if (!valsRet.ok()) {
             return valsRet.status();
@@ -484,5 +514,322 @@ Status DeleteEdgesValidator::toPlan() {
     return Status::OK();
 }
 
+Status UpdateValidator::initProps() {
+    spaceId_ = vctx_->whichSpace().id;
+    insertable_ = sentence_->getInsertable();
+    if (sentence_->getName() != nullptr) {
+        name_ = *sentence_->getName();
+    }
+    NG_RETURN_IF_ERROR(getUpdateProps());
+    NG_RETURN_IF_ERROR(getCondition());
+    return getReturnProps();
+}
+
+Status UpdateValidator::getCondition() {
+    auto *clause = sentence_->whenClause();
+    if (clause != nullptr) {
+        auto filter = clause->filter();
+        if (filter != nullptr) {
+            auto encodeStr = filter->encode();
+            auto copyFilterExpr = Expression::decode(encodeStr);
+            NG_LOG_AND_RETURN_IF_ERROR(
+                    checkAndResetSymExpr(copyFilterExpr.get(), name_, encodeStr));
+            condition_ = std::move(encodeStr);
+        }
+    }
+    return Status::OK();
+}
+
+Status UpdateValidator::getReturnProps() {
+    auto *clause = sentence_->yieldClause();
+    if (clause != nullptr) {
+        auto yields = clause->columns();
+        for (auto *col : yields) {
+            if (col->alias() == nullptr) {
+                yieldColNames_.emplace_back(col->expr()->toString());
+            } else {
+                yieldColNames_.emplace_back(*col->alias());
+            }
+            auto encodeStr = col->expr()->encode();
+            auto copyColExpr = Expression::decode(encodeStr);
+
+            NG_LOG_AND_RETURN_IF_ERROR(checkAndResetSymExpr(copyColExpr.get(), name_, encodeStr));
+            returnProps_.emplace_back(std::move(encodeStr));
+        }
+    }
+    return Status::OK();
+}
+
+Status UpdateValidator::getUpdateProps() {
+    auto status = Status::OK();
+    auto items = sentence_->updateList()->items();
+    std::unordered_set<std::string> symNames;
+    std::string fieldName;
+    const std::string *symName = nullptr;
+    for (auto& item : items) {
+        storage::cpp2::UpdatedProp updatedProp;
+        // The syntax has guaranteed it is name or expression
+        if (item->getFieldName() != nullptr) {
+            symName = &name_;
+            fieldName = *item->getFieldName();
+            symNames.emplace(name_);
+        }
+        if (item->getFieldExpr() != nullptr) {
+            DCHECK(item->getFieldExpr()->kind() == Expression::Kind::kSymProperty);
+            auto symExpr = static_cast<const SymbolPropertyExpression*>(item->getFieldExpr());
+            symNames.emplace(*symExpr->sym());
+            symName = symExpr->sym();
+            fieldName = *symExpr->prop();
+        }
+        auto valueExpr = item->value();
+        if (valueExpr == nullptr) {
+            LOG(ERROR) << "valueExpr is nullptr";
+            return Status::SyntaxError("Empty update item field value");
+        }
+        auto encodeStr = valueExpr->encode();
+        auto copyValueExpr = Expression::decode(encodeStr);
+        NG_LOG_AND_RETURN_IF_ERROR(checkAndResetSymExpr(copyValueExpr.get(), *symName, encodeStr));
+        updatedProp.set_value(std::move(encodeStr));
+        updatedProp.set_name(fieldName);
+        updatedProps_.emplace_back(std::move(updatedProp));
+    }
+
+    if (symNames.size() != 1) {
+        auto errorMsg = "Multi schema name: " + folly::join(",", symNames);
+        LOG(ERROR) << errorMsg;
+        return Status::Error(std::move(errorMsg));
+    }
+    if (symName != nullptr) {
+        name_ = *symName;
+    }
+    return status;
+}
+
+
+Status UpdateValidator::checkAndResetSymExpr(Expression* inExpr,
+                                             const std::string& symName,
+                                             std::string &encodeStr) {
+    bool hasWrongType = false;
+    auto symExpr = rewriteSymExpr(inExpr, symName, hasWrongType, isEdge_);
+    if (hasWrongType) {
+        return Status::Error("Has wrong expr in `%s'",
+                             inExpr->toString().c_str());
+    }
+    if (symExpr != nullptr) {
+        encodeStr = symExpr->encode();
+        return Status::OK();
+    }
+    encodeStr = inExpr->encode();
+    return Status::OK();
+}
+
+// rewrite the expr which has kSymProperty expr to toExpr
+std::unique_ptr<Expression> UpdateValidator::rewriteSymExpr(Expression* expr,
+                                                            const std::string &sym,
+                                                            bool &hasWrongType,
+                                                            bool isEdge) {
+    switch (expr->kind()) {
+        case Expression::Kind::kConstant: {
+            break;
+        }
+        case Expression::Kind::kAdd:
+        case Expression::Kind::kMinus:
+        case Expression::Kind::kMultiply:
+        case Expression::Kind::kDivision:
+        case Expression::Kind::kMod:
+        case Expression::Kind::kRelEQ:
+        case Expression::Kind::kRelNE:
+        case Expression::Kind::kRelLT:
+        case Expression::Kind::kRelLE:
+        case Expression::Kind::kRelGT:
+        case Expression::Kind::kRelGE:
+        case Expression::Kind::kRelIn:
+        case Expression::Kind::kLogicalAnd:
+        case Expression::Kind::kLogicalOr:
+        case Expression::Kind::kLogicalXor: {
+            auto biExpr = static_cast<BinaryExpression*>(expr);
+            auto left = rewriteSymExpr(biExpr->left(), sym, hasWrongType, isEdge);
+            if (left != nullptr) {
+                biExpr->setLeft(left.release());
+            }
+            auto right = rewriteSymExpr(biExpr->right(), sym, hasWrongType, isEdge);
+            if (right != nullptr) {
+                biExpr->setRight(right.release());
+            }
+            break;
+        }
+        case Expression::Kind::kUnaryPlus:
+        case Expression::Kind::kUnaryNegate:
+        case Expression::Kind::kUnaryNot: {
+            auto unaryExpr = static_cast<UnaryExpression*>(expr);
+            auto rewrite = rewriteSymExpr(unaryExpr->operand(), sym, hasWrongType, isEdge);
+            if (rewrite != nullptr) {
+                unaryExpr->setOperand(rewrite.release());
+            }
+            break;
+        }
+        case Expression::Kind::kFunctionCall: {
+            auto funcExpr = static_cast<FunctionCallExpression*>(expr);
+            auto* argList = const_cast<ArgumentList*>(funcExpr->args());
+            auto args = argList->moveArgs();
+            for (auto iter = args.begin(); iter < args.end(); ++iter) {
+                auto rewrite = rewriteSymExpr(iter->get(), sym, hasWrongType, isEdge);
+                if (rewrite != nullptr) {
+                    *iter = std::move(rewrite);
+                }
+            }
+            argList->setArgs(std::move(args));
+            break;
+        }
+        case Expression::Kind::kTypeCasting: {
+            auto castExpr = static_cast<TypeCastingExpression*>(expr);
+            auto operand = rewriteSymExpr(castExpr->operand(), sym, hasWrongType, isEdge);
+            if (operand != nullptr) {
+                castExpr->setOperand(operand.release());
+            }
+            break;
+        }
+        case Expression::Kind::kSymProperty: {
+            auto symExpr = static_cast<SymbolPropertyExpression*>(expr);
+            if (isEdge) {
+                return std::make_unique<EdgePropertyExpression>(
+                        new std::string(sym), new std::string(*symExpr->prop()));
+            } else {
+                if (symExpr->sym() == nullptr || !symExpr->sym()->empty()) {
+                    hasWrongType = true;
+                    return nullptr;
+                }
+                return std::make_unique<SourcePropertyExpression>(
+                        new std::string(sym), new std::string(*symExpr->prop()));
+            }
+        }
+        case Expression::Kind::kSrcProperty: {
+            if (isEdge) {
+                hasWrongType = true;
+            }
+            break;
+        }
+        case Expression::Kind::kEdgeProperty: {
+            if (!isEdge) {
+                hasWrongType = true;
+            }
+            break;
+        }
+        case Expression::Kind::kDstProperty:
+        case Expression::Kind::kTagProperty:
+        case Expression::Kind::kEdgeSrc:
+        case Expression::Kind::kEdgeRank:
+        case Expression::Kind::kEdgeDst:
+        case Expression::Kind::kEdgeType:
+        case Expression::Kind::kUUID:
+        case Expression::Kind::kVar:
+        case Expression::Kind::kVersionedVar:
+        case Expression::Kind::kVarProperty:
+        case Expression::Kind::kInputProperty:
+        case Expression::Kind::kUnaryIncr:
+        case Expression::Kind::kUnaryDecr: {
+            hasWrongType = true;
+            break;
+        }
+    }
+    return nullptr;
+}
+
+
+Status UpdateVertexValidator::validateImpl() {
+    auto sentence = static_cast<UpdateVertexSentence*>(sentence_);
+    auto idRet = SchemaUtil::toVertexID(sentence->getVid());
+    if (!idRet.ok()) {
+        LOG(ERROR) << idRet.status();
+        return idRet.status();
+    }
+    vId_ = std::move(idRet).value();
+    NG_RETURN_IF_ERROR(initProps());
+    auto ret = qctx_->schemaMng()->toTagID(spaceId_, name_);
+    if (!ret.ok()) {
+        LOG(ERROR) << "No schema found for " << name_;
+        return Status::Error("No schema found for `%s'", name_.c_str());
+    }
+    tagId_ = ret.value();
+    return Status::OK();
+}
+
+Status UpdateVertexValidator::toPlan() {
+    auto* plan = qctx_->plan();
+    auto *update = UpdateVertex::make(plan,
+                                      nullptr,
+                                      spaceId_,
+                                      std::move(name_),
+                                      vId_,
+                                      tagId_,
+                                      insertable_,
+                                      std::move(updatedProps_),
+                                      std::move(returnProps_),
+                                      std::move(condition_),
+                                      std::move(yieldColNames_));
+    root_ = update;
+    tail_ = root_;
+    return Status::OK();
+}
+
+Status UpdateEdgeValidator::validateImpl() {
+    auto sentence = static_cast<UpdateEdgeSentence*>(sentence_);
+    auto srcIdRet = SchemaUtil::toVertexID(sentence->getSrcId());
+    if (!srcIdRet.ok()) {
+        LOG(ERROR) << srcIdRet.status();
+        return srcIdRet.status();
+    }
+    srcId_ = std::move(srcIdRet).value();
+    auto dstIdRet = SchemaUtil::toVertexID(sentence->getDstId());
+    if (!dstIdRet.ok()) {
+        LOG(ERROR) << dstIdRet.status();
+        return dstIdRet.status();
+    }
+    dstId_ = std::move(dstIdRet).value();
+    rank_ = sentence->getRank();
+    NG_RETURN_IF_ERROR(initProps());
+    auto ret = qctx_->schemaMng()->toEdgeType(spaceId_, name_);
+    if (!ret.ok()) {
+        LOG(ERROR) << "No schema found for " << name_;
+        return Status::Error("No schema found for `%s'", name_.c_str());
+    }
+    edgeType_ = ret.value();
+    return Status::OK();
+}
+
+Status UpdateEdgeValidator::toPlan() {
+    auto* plan = qctx_->plan();
+    auto *outNode = UpdateEdge::make(plan,
+                                     nullptr,
+                                     spaceId_,
+                                     name_,
+                                     srcId_,
+                                     dstId_,
+                                     edgeType_,
+                                     rank_,
+                                     insertable_,
+                                     updatedProps_,
+                                     {},
+                                     condition_,
+                                     {});
+
+    auto *inNode = UpdateEdge::make(plan,
+                                    outNode,
+                                    spaceId_,
+                                    std::move(name_),
+                                    std::move(dstId_),
+                                    std::move(srcId_),
+                                    -edgeType_,
+                                    rank_,
+                                    insertable_,
+                                    std::move(updatedProps_),
+                                    std::move(returnProps_),
+                                    std::move(condition_),
+                                    std::move(yieldColNames_));
+    root_ = inNode;
+    tail_ = outNode;
+    return Status::OK();
+}
+
 }  // namespace graph
 }  // namespace nebula
diff --git a/src/validator/MutateValidator.h b/src/validator/MutateValidator.h
index 2045b29c19564930acf43915c32aaa5b1931c62c..e06cefa7257a4cee4cb93f295a66364e900edb10 100644
--- a/src/validator/MutateValidator.h
+++ b/src/validator/MutateValidator.h
@@ -110,6 +110,84 @@ private:
     std::vector<EdgeKeyRef*>                       edgeKeyRefs_;
     std::string                                    edgeKeyVar_;
 };
+
+class UpdateValidator : public Validator {
+public:
+    explicit UpdateValidator(Sentence* sentence,
+                             QueryContext* context,
+                             bool isEdge = false)
+        : Validator(sentence, context) {
+        sentence_ = static_cast<UpdateBaseSentence*>(sentence);
+        isEdge_ = isEdge;
+    }
+
+    virtual ~UpdateValidator() {}
+
+protected:
+    Status initProps();
+
+    Status getCondition();
+
+    Status getReturnProps();
+
+    Status getUpdateProps();
+
+private:
+    Status checkAndResetSymExpr(Expression* inExpr,
+                                const std::string& symName,
+                                std::string &encodeStr);
+
+    std::unique_ptr<Expression> rewriteSymExpr(Expression* expr,
+                                               const std::string &sym,
+                                               bool &hasWrongType,
+                                               bool isEdge = false);
+
+protected:
+    UpdateBaseSentence                                 *sentence_;
+    bool                                                insertable_{false};
+    GraphSpaceID                                        spaceId_{-1};
+    std::vector<std::string>                            returnProps_;
+    std::vector<std::string>                            yieldColNames_;
+    std::string                                         condition_;
+    std::vector<storage::cpp2::UpdatedProp>             updatedProps_;
+    std::string                                         name_;
+    bool                                                isEdge_{false};
+};
+
+class UpdateVertexValidator final : public UpdateValidator {
+public:
+    UpdateVertexValidator(Sentence* sentence, QueryContext* context)
+        : UpdateValidator(sentence, context) {
+    }
+
+private:
+    Status validateImpl() override;
+
+    Status toPlan() override;
+
+private:
+    std::string               vId_;
+    TagID                     tagId_{-1};
+};
+
+class UpdateEdgeValidator final : public UpdateValidator {
+public:
+    UpdateEdgeValidator(Sentence* sentence, QueryContext* context)
+        : UpdateValidator(sentence, context, true) {
+    }
+
+private:
+    Status validateImpl() override;
+
+    Status toPlan() override;
+
+private:
+    std::string                                       srcId_;
+    std::string                                       dstId_;
+    EdgeRanking                                       rank_{0};
+    EdgeType                                          edgeType_{-1};
+};
 }  // namespace graph
 }  // namespace nebula
 #endif  // VALIDATOR_MUTATEVALIDATOR_H
+
diff --git a/src/validator/Validator.cpp b/src/validator/Validator.cpp
index 17b7d22c2ec6d77ca1637eaaa81d484620c20f7d..cea013c2c08f658a9aee3b3473c040cf273cfafb 100644
--- a/src/validator/Validator.cpp
+++ b/src/validator/Validator.cpp
@@ -114,6 +114,10 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon
             return std::make_unique<DeleteVerticesValidator>(sentence, context);
         case Sentence::Kind::kDeleteEdges:
             return std::make_unique<DeleteEdgesValidator>(sentence, context);
+        case Sentence::Kind::kUpdateVertex:
+            return std::make_unique<UpdateVertexValidator>(sentence, context);
+        case Sentence::Kind::kUpdateEdge:
+            return std::make_unique<UpdateEdgeValidator>(sentence, context);
         default:
             return std::make_unique<ReportError>(sentence, context);
     }
@@ -156,7 +160,9 @@ Status Validator::appendPlan(PlanNode* node, PlanNode* appended) {
         case PlanNode::Kind::kDropSnapshot:
         case PlanNode::Kind::kShowSnapshots:
         case PlanNode::Kind::kDeleteVertices:
-        case PlanNode::Kind::kDeleteEdges: {
+        case PlanNode::Kind::kDeleteEdges:
+        case PlanNode::Kind::kUpdateVertex:
+        case PlanNode::Kind::kUpdateEdge: {
             static_cast<SingleDependencyNode*>(node)->dependsOn(appended);
             break;
         }
diff --git a/src/validator/test/MaintainValidatorTest.cpp b/src/validator/test/MaintainValidatorTest.cpp
index 8f0b68a553b63c1ae7ae62d186737216b6680b22..169e1dd56e3cb670d0c9147e10e5679a2a8ec716 100644
--- a/src/validator/test/MaintainValidatorTest.cpp
+++ b/src/validator/test/MaintainValidatorTest.cpp
@@ -28,7 +28,7 @@ TEST_F(MaintainValidatorTest, TagTest) {
     }
     // the same name schema
     {
-        ASSERT_FALSE(checkResult("CREATE TAG TEST(); CREATE TAG TEST;", {}));
+        ASSERT_FALSE(checkResult("CREATE TAG TEST();CREATE TAG TEST()", {}));
     }
 }
 
@@ -41,7 +41,7 @@ TEST_F(MaintainValidatorTest, EdgeTest) {
     }
     // the same name schema
     {
-        ASSERT_FALSE(checkResult("CREATE EDGE TEST(); CREATE EDGE TEST;", {}));
+        ASSERT_FALSE(checkResult("CREATE EDGE TEST();CREATE EDGE TEST()", {}));
     }
 }
 }  // namespace graph
diff --git a/src/validator/test/MutateValidatorTest.cpp b/src/validator/test/MutateValidatorTest.cpp
index b7b084916cf5d2991eb2cd61b9a023ae9a2896f2..357373874f4133eb16c3bbd0b92180638a9ecc46 100644
--- a/src/validator/test/MutateValidatorTest.cpp
+++ b/src/validator/test/MutateValidatorTest.cpp
@@ -19,6 +19,16 @@ TEST_F(MutateValidatorTest, InsertVertexTest) {
         auto cmd = "INSERT VERTEX person(name, age2) VALUES \"A\":(\"a\", 19);";
         ASSERT_FALSE(checkResult(cmd, {}));
     }
+    // wrong vid expression
+    {
+        auto cmd = "INSERT VERTEX person(name, age2) VALUES hash($-,name):(\"a\", 19);";
+        ASSERT_FALSE(checkResult(cmd, {}));
+    }
+    // vid use function call
+    {
+        auto cmd = "INSERT VERTEX person(name, age) VALUES lower(\"TOM\"):(\"a\", 19);";
+        ASSERT_TRUE(checkResult(cmd, { PK::kInsertVertices, PK::kStart }));
+    }
 }
 
 TEST_F(MutateValidatorTest, InsertEdgeTest) {
@@ -27,6 +37,16 @@ TEST_F(MutateValidatorTest, InsertEdgeTest) {
         auto cmd = "INSERT EDGE like(start, end2) VALUES \"A\"->\"B\":(11, 11);";
         ASSERT_FALSE(checkResult(cmd, {}));
     }
+    // wrong vid expression
+    {
+        auto cmd = "INSERT EDGE like(start, end) VALUES hash($-,name)->\"Tom\":(2010, 2020);";
+        ASSERT_FALSE(checkResult(cmd, {}));
+    }
+    // vid use function call
+    {
+        auto cmd = "INSERT EDGE like(start, end) VALUES lower(\"Lily\")->\"Tom\":(2010, 2020);";
+        ASSERT_TRUE(checkResult(cmd, { PK::kInsertEdges, PK::kStart }));
+    }
 }
 
 TEST_F(MutateValidatorTest, DeleteVertexTest) {
@@ -110,5 +130,76 @@ TEST_F(MutateValidatorTest, DeleteEdgeTest) {
         ASSERT_FALSE(checkResult(cmd));
     }
 }
+
+TEST_F(MutateValidatorTest, UpdateVertexTest) {
+    // not exist tag
+    {
+        auto cmd = "UPDATE VERTEX ON student \"Tom\" SET count = 1";
+        ASSERT_FALSE(checkResult(cmd, {}));
+    }
+    // multi tags
+    {
+        auto cmd = "UPDATE VERTEX \"Tom\" SET person.count = 1, student.age = $^.student.age + 1";
+        ASSERT_FALSE(checkResult(cmd, {}));
+    }
+    // wrong expr
+    {
+        auto cmd = "UPDATE VERTEX \"Tom\" SET person.age = person.age + 1";
+        ASSERT_FALSE(checkResult(cmd, {}));
+    }
+    // with function
+    {
+        auto cmd = "UPDATE VERTEX ON person \"Tom\" SET age = abs(age + 1)";
+        ASSERT_TRUE(checkResult(cmd, {}));
+    }
+    // 1.0 syntax succeed
+    {
+        auto cmd = "UPDATE VERTEX \"Tom\""
+                   "SET person.age = $^.person.age + 1 "
+                   "WHEN $^.person.age == 18 "
+                   "YIELD $^.person.name AS name, $^.person.age AS age";
+        ASSERT_TRUE(checkResult(cmd, {PK::kUpdateVertex, PK::kStart}));
+    }
+    // 2.0 syntax succeed
+    {
+        auto cmd = "UPDATE VERTEX ON person \"Tom\""
+                   "SET age = age + 1 "
+                   "WHEN page == 18 "
+                   "YIELD name AS name, age AS age";
+        ASSERT_TRUE(checkResult(cmd, {PK::kUpdateVertex, PK::kStart}));
+    }
+}
+
+TEST_F(MutateValidatorTest, UpdateEdgeTest) {
+    // not exist edge
+    {
+        auto cmd = "UPDATE EDGE ON study \"Tom\"->\"Lily\" SET count = 1";
+        ASSERT_FALSE(checkResult(cmd, {}));
+    }
+    // Wrong expr "$^.peson.age"
+    {
+        auto cmd = "UPDATE EDGE \"Tom\"->\"Lily\" OF like "
+                   "SET end = like.end + 1 "
+                   "WHEN $^.peson.age >= 18 "
+                   "YIELD $^.peson.age AS age, like.end AS end";
+        ASSERT_FALSE(checkResult(cmd, {}));
+    }
+    // 1.0 syntax succeed
+    {
+        auto cmd = "UPDATE EDGE \"Tom\"->\"Lily\" OF like "
+                   "SET end = like.end + 1 "
+                   "WHEN like.start >= 2010 "
+                   "YIELD like.start AS start, like.end AS end";
+        ASSERT_TRUE(checkResult(cmd, {PK::kUpdateEdge, PK::kUpdateEdge, PK::kStart}));
+    }
+    // 2.0 syntax succeed
+    {
+        auto cmd = "UPDATE EDGE ON like \"Tom\"->\"Lily\""
+                   "SET end = end + 1 "
+                   "WHEN start >= 2010 "
+                   "YIELD start AS start, end AS end";
+        ASSERT_TRUE(checkResult(cmd, {PK::kUpdateEdge, PK::kUpdateEdge, PK::kStart}));
+    }
+}
 }  // namespace graph
 }  // namespace nebula
diff --git a/src/validator/test/ValidatorTestBase.cpp b/src/validator/test/ValidatorTestBase.cpp
index 606fed112cc7a199e9154ffc2692dbd254002ab8..5330ae5169e3208fdd3677f5aeae20b60e8c2ee7 100644
--- a/src/validator/test/ValidatorTestBase.cpp
+++ b/src/validator/test/ValidatorTestBase.cpp
@@ -81,6 +81,8 @@ namespace graph {
         case PlanNode::Kind::kDropSnapshot:
         case PlanNode::Kind::kShowSnapshots:
         case PlanNode::Kind::kDataJoin:
+        case PlanNode::Kind::kUpdateVertex:
+        case PlanNode::Kind::kUpdateEdge:
             LOG(FATAL) << "Unimplemented";
         case PlanNode::Kind::kDataCollect: {
             const auto *lDC = static_cast<const DataCollect*>(l);
diff --git a/src/validator/test/ValidatorTestBase.h b/src/validator/test/ValidatorTestBase.h
index fa09d1c54f112a8b59e9c32d171520f8fce3ac41..86095a250ce95391cee4cbdc7d180d70f5b1cb97 100644
--- a/src/validator/test/ValidatorTestBase.h
+++ b/src/validator/test/ValidatorTestBase.h
@@ -188,7 +188,9 @@ protected:
                 case PlanNode::Kind::kShowSnapshots:
                 case PlanNode::Kind::kDataJoin:
                 case PlanNode::Kind::kDeleteVertices:
-                case PlanNode::Kind::kDeleteEdges: {
+                case PlanNode::Kind::kDeleteEdges:
+                case PlanNode::Kind::kUpdateVertex:
+                case PlanNode::Kind::kUpdateEdge: {
                     auto* current = static_cast<const SingleInputNode*>(node);
                     queue.emplace(current->dep());
                     break;
diff --git a/src/validator/test/YieldValidatorTest.cpp b/src/validator/test/YieldValidatorTest.cpp
index 478cf0e918db38285dfc3b04c9587ceaf0cdeac4..9a272b49c8df4464b303d906f06ecaa743e3f3f7 100644
--- a/src/validator/test/YieldValidatorTest.cpp
+++ b/src/validator/test/YieldValidatorTest.cpp
@@ -153,10 +153,10 @@ TEST_F(YieldValidatorTest, TypeCastTest) {
         EXPECT_TRUE(checkResult(query, expected_));
     }
     {
-        std::string query = "YIELD (int30)\"-123\"";
+        std::string query = "YIELD (int30)(\"-123\")";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                   "SyntaxError: syntax error near `)\"-123\"'");
+                   "SyntaxError: syntax error near `(\"-123\")'");
     }
     {
         std::string query = "YIELD (int)\"123abc\"";
@@ -209,10 +209,10 @@ TEST_F(YieldValidatorTest, TypeCastTest) {
         EXPECT_TRUE(checkResult(query, expected_));
     }
     {
-        std::string query = "YIELD (MAP)\"12\"";
+        std::string query = "YIELD (MAP)(\"12\")";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                   "SyntaxError: syntax error near `)\"12\"'");
+                   "SyntaxError: syntax error near `(\"12\")'");
     }
     {
         std::string query = "YIELD (SET)12";
@@ -224,13 +224,13 @@ TEST_F(YieldValidatorTest, TypeCastTest) {
         std::string query = "YIELD (PATH)true";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                   "SyntaxError: syntax error near `)true'");
+                   "SyntaxError: syntax error near `true'");
     }
     {
         std::string query = "YIELD (NOEXIST)true";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                   "SyntaxError: syntax error near `)true'");
+                   "SyntaxError: syntax error near `true'");
     }
 }
 
diff --git a/tests/mutate/test_insert_2.py b/tests/mutate/test_insert_2.py
index 5340e907ce38f7230818b98be44bfe3bbcbabf0c..8cb188e66222cdf16e7cb75b12339652720f379a 100644
--- a/tests/mutate/test_insert_2.py
+++ b/tests/mutate/test_insert_2.py
@@ -46,6 +46,10 @@ class TestInsert2(NebulaTestSuite):
         resp = self.execute('INSERT VERTEX course(name) VALUES "English":("English")')
         self.check_resp_succeeded(resp)
 
+    def test_insert_with_name_label(self):
+        resp = self.execute('INSERT VERTEX course(name) VALUES "English":(English)')
+        self.check_resp_failed(resp)
+
     @pytest.mark.skip(reason="does not support fetch")
     def test_insert_with_fix_string(self):
         resp = self.execute('FETCH PROP ON course "English"')
diff --git a/tests/mutate/test_update_upsert.py b/tests/mutate/test_update_upsert.py
new file mode 100644
index 0000000000000000000000000000000000000000..164ebaf6eb88e285b1449b17ff1705bfe9c71e34
--- /dev/null
+++ b/tests/mutate/test_update_upsert.py
@@ -0,0 +1,659 @@
+# --coding:utf-8--
+#
+# Copyright (c) 2020 vesoft inc. All rights reserved.
+#
+# This source code is licensed under Apache 2.0 License,
+# attached with Common Clause Condition 1.0, found in the LICENSES directory.
+
+import time
+import pytest
+
+from tests.common.nebula_test_suite import NebulaTestSuite
+
+class TestUpdateVertex(NebulaTestSuite):
+    @classmethod
+    def prepare(self):
+        resp = self.execute('CREATE SPACE myspace_test2(partition_num=1, replica_factor=1, vid_size=20);'
+                            'USE myspace_test2;'
+                            'CREATE TAG course(name string, credits int);'
+                            'CREATE TAG building(name string);'
+                            'CREATE TAG student(name string, age int, gender string);'
+                            'CREATE TAG student_default(name string NOT NULL, age int NOT NULL, '
+                            'gender string DEFAULT "one", birthday int DEFAULT 2010);'
+                            'CREATE EDGE like(likeness double);'
+                            'CREATE EDGE select(grade int, year int);'
+                            'CREATE EDGE select_default(grade int NOT NULL, '
+                            'year TIMESTAMP DEFAULT 1546308000)')
+        self.check_resp_succeeded(resp)
+        time.sleep(self.delay)
+
+        resp = self.execute('INSERT VERTEX student(name, age, gender) VALUES  '
+                            '"200":("Monica", 16, "female"),'
+                            '"201":("Mike", 18, "male"),'
+                            '"202":("Jane", 17, "female");')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('INSERT VERTEX course(name, credits),building(name) VALUES '
+                            '"101":("Math", 3, "No5"), '
+                            '"102":("English", 6, "No11");')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('INSERT VERTEX course(name, credits) VALUES "103":("CS", 5);')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute('INSERT VERTEX student(name, age, gender) VALUES '
+        #                     'uuid("Monica"):("Monica", 16, "female"), '
+        #                     'uuid("Mike"):("Mike", 18, "male"), '
+        #                     'uuid("Jane"):("Jane", 17, "female");')
+        # self.check_resp_succeeded(resp)
+
+        # resp = self.execute('INSERT VERTEX course(name, credits),building(name) VALUES '
+        #                     'uuid("Math"):("Math", 3, "No5"), '
+        #                     'uuid("English"):("English", 6, "No11");')
+        # self.check_resp_succeeded(resp)
+
+        # resp = self.execute('INSERT VERTEX course(name, credits) VALUES uuid("CS"):("CS", 5);')
+        # self.check_resp_succeeded(resp)
+
+        resp = self.execute('INSERT EDGE select(grade, year) VALUES '
+                            '"200" -> "101"@0:(5, 2018), '
+                            '"200" -> "102"@0:(3, 2018), '
+                            '"201" -> "102"@0:(3, 2019), '
+                            '"202" -> "102"@0:(3, 2019);')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('INSERT EDGE like(likeness) VALUES '
+                            '"200" -> "201"@0:(92.5), '
+                            '"201" -> "200"@0:(85.6), '
+                            '"201" -> "202"@0:(93.2);')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute('INSERT EDGE select(grade, year) VALUES '
+        #                     'uuid("Monica") -> uuid("Math")@0:(5, 2018), '
+        #                     'uuid("Monica") -> uuid("English")@0:(3, 2018), '
+        #                     'uuid("Mike") -> uuid("English")@0:(3, 2019), '
+        #                     'uuid("Jane") -> uuid("English")@0:(3, 2019);')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute('INSERT EDGE like(likeness) VALUES '
+        #                     'uuid("Monica") -> uuid("Mike")@0:(92.5), '
+        #                     'uuid("Monica") -> uuid("English")@0:(3, 2018), '
+        #                     'uuid("Mike") -> uuid("Monica")@0:(85.6), '
+        #                     'uuid("Mike") -> uuid("Jane")@0:(93.2)')
+        # self.check_resp_succeeded(resp)
+
+    @classmethod
+    def cleanup(self):
+        resp = self.execute('DROP SPACE myspace_test2;')
+        self.check_resp_succeeded(resp)
+
+    def test_update_vertex(self):
+        resp = self.execute('UPDATE VERTEX "101" '
+                            'SET course.credits = $^.course.credits + 1;')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute('UPDATE VERTEX ON course uuid("Math") '
+        #                     'SET credits = $^.course.credits + 1;')
+        # self.check_resp_succeeded(resp)
+
+        # filter out
+        resp = self.execute('UPDATE VERTEX "101" '
+                            'SET course.credits = $^.course.credits + 1 '
+                            'WHEN $^.course.name == "English" && $^.course.credits > 2')
+        self.check_resp_succeeded(resp)
+
+        # set filter
+        resp = self.execute('UPDATE VERTEX "101" '
+                            'SET course.credits = $^.course.credits + 1 '
+                            'WHEN $^.course.name == "Math" && $^.course.credits > 2')
+        self.check_resp_succeeded(resp)
+
+        # update with uuid
+        # resp = self.execute('UPDATE VERTEX ON course uuid("Math") '
+        #                     'SET credits = $^.course.credits + 1 '
+        #                     'WHEN $^.course.name == "Math" && $^.course.credits > 2')
+        # self.check_resp_succeeded(resp)
+
+        # set yield
+        resp = self.execute_query('UPDATE VERTEX "101" '
+                                  'SET course.credits = $^.course.credits + 1 '
+                                  'YIELD $^.course.name AS Name, $^.course.credits AS Credits')
+        self.check_resp_succeeded(resp)
+        expected_result = [["Math", 6]]
+        self.check_result(resp, expected_result)
+
+        # set yield with uuid
+        # resp = self.execute_query('UPDATE VERTEX ON course uuid("Math") '
+        #                           'SET credits = $^.course.credits + 1 '
+        #                           'YIELD $^.course.name AS Name, $^.course.credits AS Credits')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Math", 6]]
+        # self.check_result(resp, expected_result)
+
+        # set filter and yield
+        resp = self.execute_query('UPDATE VERTEX "101" '
+                                  'SET course.credits = $^.course.credits + 1 '
+                                  'WHEN $^.course.name == "Math" && $^.course.credits > 2 '
+                                  'YIELD $^.course.name AS Name, $^.course.credits AS Credits')
+        self.check_resp_succeeded(resp)
+        expected_result = [["Math", 7]]
+        self.check_result(resp, expected_result)
+
+        # set filter and yield with uuid
+        # resp = self.execute_query('UPDATE VERTEX ON course uuid("Math") '
+        #                           'SET credits = $^.course.credits + 1 '
+        #                           'WHEN $^.course.name == "Math" && $^.course.credits > 2 '
+        #                           'YIELD $^.course.name AS Name, $^.course.credits AS Credits')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Math", 7]]
+        # self.check_result(resp, expected_result)
+
+        # set filter out and yield
+        resp = self.execute_query('UPDATE VERTEX "101" '
+                                  'SET course.credits = $^.course.credits + 1 '
+                                  'WHEN $^.course.name == "notexist" && $^.course.credits > 2'
+                                  'YIELD $^.course.name AS Name, $^.course.credits AS Credits')
+        self.check_resp_succeeded(resp)
+        expected_result = [["Math", 7]]
+        self.check_result(resp, expected_result)
+
+        # set filter out and yield with uuid
+        # resp = self.execute_query('UPDATE VERTEX ON course uuid("Math") '
+        #                           'SET credits = $^.course.credits + 1 '
+        #                           'WHEN $^.course.name == "notexist" && $^.course.credits > 2'
+        #                           'YIELD $^.course.name AS Name, $^.course.credits AS Credits')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Math", 7]]
+        # self.check_result(resp, expected_result)
+
+    def test_update_edge(self):
+        # resp = self.execute_query('FETCH PROP "200"->"101"@0 OF select '
+        #                           'YIELD select.grade, select.year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [[200, 101, 0, 5, 2018]]
+        # self.check_result(resp, expected_result)
+
+        # update edge
+        resp = self.execute('UPDATE EDGE "200" -> "101"@0 OF select '
+                            'SET grade = select.grade + 1, year = 2000;')
+        self.check_resp_succeeded(resp)
+
+        # check
+        # resp = self.execute_query('FETCH PROP ON select "200"->"101"@0 '
+        #                           'YIELD select.grade, select.year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [[200, 101, 0, 6, 2000]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute_query('GO FROM "101" OVER select REVERSELY '
+                                  'YIELD select.grade, select.year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[6, 2000]]
+        self.check_result(resp, expected_result)
+
+        # update edge with uuid
+        # resp = self.execute('UPDATE EDGE ON select uuid("Monica") -> uuid("Math")@0 '
+        #                     'SET grade = select.grade + 1, year = 2000;')
+        # self.check_resp_succeeded(resp)
+
+        # filter out, 2.0 storage not support update edge can use vertex
+        resp = self.execute('UPDATE EDGE "200" -> "101"@0 OF select '
+                            'SET grade = select.grade + 1, year = 2000 '
+                            'WHEN select.grade > 1024 && $^.student.age > 15;')
+        self.check_resp_failed(resp)
+
+        # 2.0 test, filter out
+        resp = self.execute('UPDATE EDGE "200" -> "101"@0 OF select '
+                            'SET grade = select.grade + 1, year = 2000 '
+                            'WHEN select.grade > 1024')
+        self.check_resp_succeeded(resp)
+
+        # set filter, 2.0 storage not support update edge can use vertex
+        resp = self.execute('UPDATE EDGE "200" -> "101"@0 OF select '
+                            'SET grade = select.grade + 1, year = 2000 '
+                            'WHEN select.grade > 4 && $^.student.age > 15;')
+        self.check_resp_failed(resp)
+
+        # set filter
+        resp = self.execute('UPDATE EDGE "200" -> "101"@0 OF select '
+                            'SET grade = select.grade + 1, year = 2000 '
+                            'WHEN select.grade > 4')
+        self.check_resp_succeeded(resp)
+
+
+        # set filter
+        # resp = self.execute('UPDATE EDGE ON select uuid("Monica") -> uuid("Math")@0 '
+        #                     'SET grade = select.grade + 1, year = 2000'
+        #                     'WHEN select.grade > 4 && $^.student.age > 15;')
+        self.check_resp_succeeded(resp)
+
+        # set yield, 2.0 storage not support update edge can use vertex
+        resp = self.execute_query('UPDATE EDGE "200" -> "101"@0 OF select  '
+                            'SET grade = select.grade + 1, year = 2018 '
+                            'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+
+        # set yield
+        resp = self.execute_query('UPDATE EDGE "200" -> "101"@0 OF select '
+                            'SET grade = select.grade + 1, year = 2018 '
+                            'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[8, 2018]]
+        self.check_result(resp, expected_result)
+
+        # set yield
+        # resp = self.execute('UPDATE EDGE ON select uuid("Monica") -> uuid("Math"@0 '
+        #                     'SET grade = select.grade + 1, year = 2018 '
+        #                     'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Monica", 8, 2018]]
+        # self.check_result(resp, expected_result)
+
+        # set filter and yield, 2.0 storage not support update edge can use vertex
+        resp = self.execute_query('UPDATE EDGE "200" -> "101"@0 OF select '
+                            'SET grade = select.grade + 1, year = 2019 '
+                            'WHEN select.grade > 4 && $^.student.age > 15 '
+                            'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        resp = self.execute_query('UPDATE EDGE "200" -> "101"@0 OF select '
+                            'SET grade = select.grade + 1, year = 2019 '
+                            'WHEN select.grade > 4 '
+                            'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[9, 2019]]
+        self.check_result(resp, expected_result)
+
+        # set filter and yield with uuid
+        # resp = self.execute('UPDATE EDGE ON select uuid("Monica") -> uuid("Math"@0 '
+        #                     'SET grade = select.grade + 1, year = 2018 '
+        #                     'WHEN select.grade > 4 && $^.student.age > 15 '
+        #                     'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Monica", 9, 2019]]
+        # self.check_result(resp, expected_result)
+
+
+        # set filter out and yield, 2.0 storage not support update edge can use vertex
+        resp = self.execute_query('UPDATE EDGE "200" -> "101"@0 OF select '
+                            'SET grade = select.grade + 1, year = 2019 '
+                            'WHEN select.grade > 233333333333 && $^.student.age > 15 '
+                            'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        # set filter out and yield
+        resp = self.execute_query('UPDATE EDGE "200" -> "101"@0 OF select '
+                                  'SET grade = select.grade + 1, year = 2019 '
+                                  'WHEN select.grade > 233333333333'
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[9, 2019]]
+        self.check_result(resp, expected_result)
+
+        # set filter out and yield
+        # resp = self.execute('UPDATE EDGE ON select uuid("Monica") -> uuid("Math"@0 '
+        #                     'SET grade = select.grade + 1, year = 2018 '
+        #                     'WHEN select.grade > 233333333333 && $^.student.age > 15 '
+        #                     'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Monica", 9, 2019]]
+        # self.check_result(resp, expected_result)
+
+    def test_update_vertex_failed(self):
+        # update vertex: the item is TagName.PropName = Expression in SET clause
+        resp = self.execute_query('UPDATE VERTEX "101" '
+                                  'SET course.credits = $^.course.credits + 1, course.name = "No9"')
+        self.check_resp_succeeded(resp)
+
+        # the $$.TagName.PropName expressions are not allowed in any update sentence
+        resp = self.execute_query('UPDATE VERTEX "101" '
+                                  'SET course.credits = $$.course.credits + 1 '
+                                  'WHEN $$.course.name == "Math" && $^.course.credits > $$.course.credits + 1 '
+                                  'YIELD $^.course.name AS Name, $^.course.credits AS Credits, $$.building.name')
+        self.check_resp_failed(resp)
+
+        # make sure TagName and PropertyName must exist in all clauses
+        resp = self.execute_query('UPDATE VERTEX "101" '
+                                  'SET nonexistentTag.credits = $^.nonexistentTag.credits + 1')
+        self.check_resp_failed(resp)
+
+    def test_update_edge_failed(self):
+        # update edge: the item is PropName = Expression in SET clause
+        resp = self.execute_query('UPDATE EDGE "200" -> "101"@0 OF select '
+                                  'SET select = select.grade + 1, select.year = 2019')
+        self.check_resp_failed(resp)
+
+        # make sure EdgeName and PropertyName must exist in all clauses
+        resp = self.execute_query('UPDATE EDGE "200" -> "101"@0 OF select '
+                                  'SET nonexistentProperty = select.grade + 1, year = 2019 '
+                                  'WHEN nonexistentEdgeName.grade > 4 && $^.student.nonexistentProperty > 15 '
+                                  'YIELD $^.nonexistentTag.name AS Name, select.nonexistentProperty AS Grade')
+        self.check_resp_failed(resp)
+
+        # make sure the edge_type must not exist
+        resp = self.execute_query('UPDATE EDGE "200" -> "101"@0 OF nonexistentEdgeTypeName '
+                                  'SET grade = nonexistentEdgeTypeName.grade + 1, year = 2019')
+        self.check_resp_failed(resp)
+
+
+    def test_upsert_vertex(self):
+        # vertex not exist
+        # resp = self.execute_query('FETCH PROP ON course "103" YIELD course.name, course.credits')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["103", "CS", 5]]
+        # self.check_result(resp, expected_result)
+
+        # not allow to handle multi tagid when update
+        resp = self.execute('UPDATE VERTEX "103" '
+                            'SET course.credits = $^.course.credits + 1, name = $^.building.name')
+        self.check_resp_failed(resp)
+
+        # update: vertex 103 ("CS", 5) --> ("CS", 6), TODO: storage not ready
+        # resp = self.execute_query('UPDATE VERTEX "103" '
+        #                           'SET course.credits = $^.course.credits + 1 '
+        #                           'WHEN $^.course.name == "CS" && $^.course.credits > 2 '
+        #                           "YIELD $^.course.name AS Name, $^.course.credits AS Credits")
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["CS", 6]]
+        # self.check_result(resp, expected_result)
+
+        # when tag on vertex not exists, update failed
+        resp = self.execute_query('UPDATE VERTEX "104" '
+                                  'SET course.credits = $^.course.credits + 1  '
+                                  'WHEN $^.course.name == "CS" && $^.course.credits > 2 '
+                                  "YIELD $^.course.name AS Name, $^.course.credits AS Credits")
+        self.check_resp_failed(resp)
+
+        # has default value test,
+        # Insertable: vertex 110 ("Ann") --> ("Ann", "one"),
+        # 110 is nonexistent, gender with default value, name and age without default value
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT VERTEX "110" '
+        #                           'SET student_default.name = "Ann", student_default.age = 10 '
+        #                           "YIELD $^.student_default.name AS Name, $^.student_default.gender AS Gender")
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Ann", "one"]]
+        # self.check_result(resp, expected_result)
+
+        # Insertable failed, 111 is nonexistent, name and age without default value
+        resp = self.execute_query('UPSERT VERTEX "111" '
+                                  'SET student_default.name = "Tom", '
+                                  'age = $^.student_default.age + 8 '
+                                  "YIELD $^.student_default.name AS Name, $^.student_default.age AS Age")
+        self.check_resp_failed(resp)
+
+        # Insertable failed, 111 is nonexistent, name without default value
+        resp = self.execute_query('UPSERT VERTEX ON student_default "111" '
+                                  'SET student_default.gender = "two", age = 10 '
+                                  "YIELD $^.student_default.name AS Name, $^.student_default.gender AS Gender")
+        self.check_resp_failed(resp)
+
+        # Insertable: vertex 112 ("Lily") --> ("Lily", "one")
+        # 112 is nonexistent, gender with default value
+        # update student_default.age with string value
+        resp = self.execute_query('UPSERT VERTEX "112" '
+                                  'SET student_default.name = "Lily", age = "10"'
+                                  "YIELD $^.student_default.name AS Name, $^.student_default.gender AS Gender")
+        self.check_resp_failed(resp)
+
+        # Insertable: vertex 113 ("Jack") --> ("Jack", "Three")
+        # 113 is nonexistent, gender with default value,
+        # update student_default.age with string value
+        resp = self.execute_query('UPSERT VERTEX "113" '
+                                  'SET student_default.name = "Ann", age = "10"'
+                                  "YIELD $^.student_default.name AS Name, $^.student_default.gender AS Gender")
+        self.check_resp_failed(resp)
+
+        # when tag on vertex not exists, update failed
+        resp = self.execute_query('UPDATE VERTEX "104" '
+                                  'SET course.credits = $^.course.credits + 1 '
+                                  'WHEN $^.course.name == \"CS\" && $^.course.credits > 2 '
+                                  'YIELD $^.course.name AS Name, $^.course.credits AS Credits')
+        self.check_resp_failed(resp)
+
+        # Insertable success, 115 is nonexistent, name and age without default value,
+        # the filter is always true.
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT VERTEX "115" '
+        #                           'SET student_default.name = "Kate", student_default.age = 12 '
+        #                           'WHEN $^.student_default.gender == "two"'
+        #                           "YIELD $^.student_default.name AS Name, $^.student_default.age AS Age, "
+        #                           "$^.student_default.gender AS gender")
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Kate", 12, "one"]]
+        # self.check_result(resp, expected_result)
+
+        # Order problem
+        # Insertable success, 116 is nonexistent, name and age without default value,
+        # the filter is always true.
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT VERTEX "116" '
+        #                           'SET student_default.name = "Kate", student_default.age = $^.student_default.birthday + 1,'
+        #                           'student_default.birthday = $^.student_default.birthday + 1 '
+        #                           'WHEN $^.student_default.gender == "two"'
+        #                           'YIELD $^.student_default.name AS Name, $^.student_default.age AS Age, '
+        #                           '$^.student_default.gender AS gender, $^.student_default.birthday AS birthday')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Kate", 2011, "one", 2011]]
+        # self.check_result(resp, expected_result)
+
+        # Order problem
+        # Insertable success, 117 is nonexistent, name and age without default value,
+        # the filter is always true.
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT VERTEX "117" '
+        #                           'SET student_default.birthday = $^.student_default.birthday + 1,'
+        #                           'student_default.name = "Kate", '
+        #                           'student_default.age = $^.student_default.birthday + 1 '
+        #                           'YIELD $^.student_default.name AS Name, $^.student_default.age AS Age, '
+        #                           '$^.student_default.gender AS gender, $^.student_default.birthday AS birthday')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Kate", 2012, "one", 2011]]
+        # self.check_result(resp, expected_result)
+
+    def test_upsert_edge(self):
+        # resp = self.execute_query('FETCH PROP ON select "200"->"101"@0 YIELD select.grade, select.year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["200", "101", 0, 5, 2018]]
+        # self.check_result(resp, expected_result)
+
+        # Insertable, upsert when edge exists, 2.0 storage not support update edge with vertex prop
+        resp = self.execute_query('UPSERT EDGE ON select "201" -> "101"@0'
+                                  'SET grade = 3, year = 2019 '
+                                  'WHEN $^.student.age > 15 && $^.student.gender == "male" '
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        # Insertable, upsert when edge not exist
+        resp = self.execute_query('UPSERT EDGE ON select "201" -> "101"@0'
+                                  'SET grade = 3, year = 2019 '
+                                  'WHEN select.grade > 3 '
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[3, 2019]]
+        self.check_result(resp, expected_result)
+
+        # update when edge not exists, failed
+        resp = self.execute_query('UPDATE EDGE ON select "601" -> "101"@0'
+                                  'SET year = 2019 '
+                                  'WHEN select.grade >10 '
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        # upsert when edge not exists,success
+        # filter condition is always true, insert default value or update value
+        resp = self.execute_query('UPSERT EDGE ON select "601" -> "101"@0'
+                                  'SET year = 2019, grade = 3 '
+                                  'WHEN select.grade >10 '
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[3, 2019]]
+        self.check_result(resp, expected_result)
+
+        # resp = self.execute_query('FETCH PROP ON select 601->101@0 YIELD select.grade, select.year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["601", "101", 0, 3, 2019]]
+        # self.check_result(resp, expected_result)
+
+        # select_default's year with default value, timestamp not support
+        # resp = self.execute_query('UPSERT EDGE ON select_default "111" -> "222"@0 '
+        #                           'SET grade = 3 '
+        #                           'YIELD select_default.grade AS Grade, select_default.year AS Year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [[3, 1546308000]]
+        # self.check_result(resp, expected_result)
+
+        # select_default's year is timestamp type, set str type, , timestamp not support
+        # resp = self.execute_query('UPSERT EDGE ON select_default "222" -> "333"@0 '
+        #                           'SET grade = 3, year = "2020-01-10 10:00:00" '
+        #                           'YIELD select_default.grade AS Grade, select_default.year AS Year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [[3, 1578621600]]
+        # self.check_result(resp, expected_result)
+
+        # select_default's grade without default value
+        resp = self.execute_query('UPSERT EDGE ON select_default "444" -> "555"@0 '
+                                  'SET year = 1546279201 '
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        # select_default's grade without default value
+        resp = self.execute_query('UPSERT EDGE ON select_default "333" -> "444"@0 '
+                                  'SET grade = 3 + select_default.grade '
+                                  'YIELD select_default.grade AS Grade, select_default.year AS Year')
+        self.check_resp_failed(resp)
+
+        # update select_default's year with edge prop value, grade is not null value and without default value
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT EDGE ON select_default "222" -> "444"@0 '
+        #                           'SET grade = 3, year = select_default.year + 10 '
+        #                           'YIELD select_default.grade AS Grade, select_default.year AS Year')
+        # self.check_resp_succeeded(resp)
+
+        # TODO: timestamp has not supported
+        # expected_result = [[3, 1546308010]]
+        # self.check_result(resp, expected_result)
+
+    def test_update_not_exist(self):
+        # make sure the vertex must not exist
+        resp = self.execute_query('UPDATE VERTEX "1010000" '
+                                  'SET course.credits = $^.course.credits + 1, name = "No9" '
+                                  'WHEN $^.course.name == "Math" && $^.course.credits > 2 '
+                                  'YIELD select_default.grade AS Grade, select_default.year AS Year')
+        self.check_resp_failed(resp)
+
+        # make sure the edge(src, dst) must not exist
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101000000000000"@0  '
+                                  'SET grade = select.grade + 1, year = 2019 '
+                                  'WHEN select.grade > 4 && $^.student.age > 15 '
+                                  'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        # make sure the edge(src, ranking, dst) must not exist
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@123456789  '
+                                  'SET grade = select.grade + 1, year = 2019 '
+                                  'WHEN select.grade > 4 && $^.student.age > 15 '
+                                  'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+        self.check_resp_failed(resp)
+
+    def test_upsert_then_insert(self):
+        resp = self.execute_query('UPSERT VERTEX "100" SET building.name = "No1"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "100"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100", "No1"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('INSERT VERTEX building(name) VALUES "100": ("No2")')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "100"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100", "No2"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute_query('UPSERT VERTEX "101" SET building.name = "No1"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "101"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["101", "No1"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('INSERT VERTEX building(name) VALUES "101": ("No2")')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "101"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["101", "No2"]]
+        # self.check_result(resp, expected_result)
+
+    def test_upsert_after_alter_schema(self):
+        resp = self.execute('INSERT VERTEX building(name) VALUES "100": ("No1")')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('ALTER TAG building ADD (new_field string default "123")')
+        self.check_resp_succeeded(resp)
+
+        time.sleep(self.delay)
+
+        resp = self.execute('UPSERT VERTEX "100" SET building.name = "No2"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "100"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100", "No2", "123"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('UPSERT VERTEX "100" SET building.name = "No3", building.new_field = "321"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "101"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100", "No3", "321"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('UPSERT VERTEX "101" SET building.name = "No1", building.new_field = "No2"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "101"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100",  "No1", "No2"]]
+        # self.check_result(resp, expected_result)
+
+        # Test upsert edge after alter schema
+        resp = self.execute('INSERT EDGE like(likeness) VALUES "1" -> "100":(1.0)')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('ALTER EDGE like ADD (new_field string default "123")')
+        self.check_resp_succeeded(resp)
+
+        time.sleep(self.delay)
+
+        resp = self.execute('UPSERT EDGE "1"->"100" OF like SET likeness = 2.0')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP ON like "1"->"100"@0')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["1", "100", 0, 2.0, "123"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('UPSERT EDGE "1"->"100" OF like SET likeness = 3.0, new_field = "321"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP ON like "1"->"100"@0')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["1", "100", 0, 3.0, "321"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('UPSERT EDGE "1"->"101" OF like SET likeness = 1.0, new_field = "111"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP ON like "1"->"101"@0')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["1", "101", 0, 1.0, "111"]]
+        # self.check_result(resp, expected_result)
+
diff --git a/tests/mutate/test_update_upsert_2.py b/tests/mutate/test_update_upsert_2.py
new file mode 100644
index 0000000000000000000000000000000000000000..61ed472c234c586f5d6a9d9cae712822c4d9616e
--- /dev/null
+++ b/tests/mutate/test_update_upsert_2.py
@@ -0,0 +1,552 @@
+# --coding:utf-8--
+#
+# Copyright (c) 2020 vesoft inc. All rights reserved.
+#
+# This source code is licensed under Apache 2.0 License,
+# attached with Common Clause Condition 1.0, found in the LICENSES directory.
+
+import time
+import pytest
+
+from tests.common.nebula_test_suite import NebulaTestSuite
+
+class TestUpdateVertex(NebulaTestSuite):
+    @classmethod
+    def prepare(self):
+        resp = self.execute('CREATE SPACE myspace_test_update(partition_num=1, replica_factor=1, vid_size=20);'
+                            'USE myspace_test_update;'
+                            'CREATE TAG course(name string, credits int);'
+                            'CREATE TAG building(name string);'
+                            'CREATE TAG student(name string, age int, gender string);'
+                            'CREATE TAG student_default(name string NOT NULL, age int NOT NULL, '
+                            'gender string DEFAULT "one", birthday int DEFAULT 2010);'
+                            'CREATE EDGE like(likeness double);'
+                            'CREATE EDGE select(grade int, year int);'
+                            'CREATE EDGE select_default(grade int NOT NULL, '
+                            'year TIMESTAMP DEFAULT 1546308000)')
+        self.check_resp_succeeded(resp)
+        time.sleep(self.delay)
+
+        resp = self.execute('INSERT VERTEX student(name, age, gender) VALUES  '
+                            '"200":("Monica", 16, "female"),'
+                            '"201":("Mike", 18, "male"),'
+                            '"202":("Jane", 17, "female");')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('INSERT VERTEX course(name, credits),building(name) VALUES '
+                            '"101":("Math", 3, "No5"), '
+                            '"102":("English", 6, "No11");')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('INSERT VERTEX course(name, credits) VALUES "103":("CS", 5);')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('INSERT EDGE select(grade, year) VALUES '
+                            '"200" -> "101"@0:(5, 2018), '
+                            '"200" -> "102"@0:(3, 2018), '
+                            '"201" -> "102"@0:(3, 2019), '
+                            '"202" -> "102"@0:(3, 2019);')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('INSERT EDGE like(likeness) VALUES '
+                            '"200" -> "201"@0:(92.5), '
+                            '"201" -> "200"@0:(85.6), '
+                            '"201" -> "202"@0:(93.2);')
+        self.check_resp_succeeded(resp)
+
+    @classmethod
+    def cleanup(self):
+        resp = self.execute('DROP SPACE myspace_test_update;')
+        self.check_resp_succeeded(resp)
+
+    def test_update_vertex(self):
+        resp = self.execute('UPDATE VERTEX ON course "101" '
+                            'SET credits = credits + 1;')
+        self.check_resp_succeeded(resp)
+
+        # filter out
+        resp = self.execute('UPDATE VERTEX ON course "101" '
+                            'SET credits = credits + 1 '
+                            'WHEN name == "English" && credits > 2')
+        self.check_resp_succeeded(resp)
+
+        # set filter
+        resp = self.execute('UPDATE VERTEX ON course "101" '
+                            'SET credits = credits + 1 '
+                            'WHEN name == "Math" && credits > 2')
+        self.check_resp_succeeded(resp)
+
+        # set yield
+        resp = self.execute_query('UPDATE VERTEX ON course "101" '
+                                  'SET credits = credits + 1 '
+                                  'YIELD name AS Name, credits AS Credits')
+        self.check_resp_succeeded(resp)
+        expected_result = [["Math", 6]]
+        self.check_result(resp, expected_result)
+
+        # set filter and yield
+        resp = self.execute_query('UPDATE VERTEX ON course "101" '
+                                  'SET credits = credits + 1 '
+                                  'WHEN name == "Math" && credits > 2 '
+                                  'YIELD name AS Name, credits AS Credits')
+        self.check_resp_succeeded(resp)
+        expected_result = [["Math", 7]]
+        self.check_result(resp, expected_result)
+
+        # set filter out and yield
+        resp = self.execute_query('UPDATE VERTEX ON course "101" '
+                                  'SET credits = credits + 1 '
+                                  'WHEN name == "notexist" && credits > 2'
+                                  'YIELD name AS Name, credits AS Credits')
+        self.check_resp_succeeded(resp)
+        expected_result = [["Math", 7]]
+        self.check_result(resp, expected_result)
+
+
+    def test_update_edge(self):
+        # resp = self.execute_query('FETCH PROP ON select "200"->"101"@0 '
+        #                           'YIELD select.grade, select.year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [[200, 101, 0, 5, 2018]]
+        # self.check_result(resp, expected_result)
+
+        # update edge
+        resp = self.execute('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = grade + 1, year = 2000;')
+        self.check_resp_succeeded(resp)
+
+        # check
+        # resp = self.execute_query('FETCH PROP ON select "200"->"101"@0 '
+        #                           'YIELD select.grade, select.year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [[200, 101, 0, 6, 2000]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute_query('GO FROM "101" OVER select REVERSELY '
+                                  'YIELD select.grade, select.year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[6, 2000]]
+        self.check_result(resp, expected_result)
+
+        # filter out, 2.0 storage not support update edge can use vertex
+        resp = self.execute('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = select.grade + 1, year = 2000 '
+                            'WHEN select.grade > 1024 && $^.student.age > 15;')
+        self.check_resp_failed(resp)
+
+        # 2.0 test, filter out
+        resp = self.execute('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = select.grade + 1, year = 2000 '
+                            'WHEN select.grade > 1024')
+        self.check_resp_succeeded(resp)
+
+        # set filter, 2.0 storage not support update edge can use vertex
+        resp = self.execute('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = select.grade + 1, year = 2000 '
+                            'WHEN select.grade > 4 && $^.student.age > 15;')
+        self.check_resp_failed(resp)
+
+        # set filter
+        resp = self.execute('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = select.grade + 1, year = 2000 '
+                            'WHEN select.grade > 4')
+        self.check_resp_succeeded(resp)
+
+        # set yield, 2.0 storage not support update edge can use vertex
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = select.grade + 1, year = 2018 '
+                            'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+
+        # set yield
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = select.grade + 1, year = 2018 '
+                            'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[8, 2018]]
+        self.check_result(resp, expected_result)
+
+        # set filter and yield, 2.0 storage not support update edge can use vertex
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = select.grade + 1, year = 2019 '
+                            'WHEN select.grade > 4 && $^.student.age > 15 '
+                            'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = select.grade + 1, year = 2019 '
+                            'WHEN select.grade > 4 '
+                            'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[9, 2019]]
+        self.check_result(resp, expected_result)
+
+        # set filter out and yield, 2.0 storage not support update edge can use vertex
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@0 '
+                            'SET grade = select.grade + 1, year = 2019 '
+                            'WHEN select.grade > 233333333333 && $^.student.age > 15 '
+                            'YIELD $^.student.name AS Name, select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        # set filter out and yield
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@0 '
+                                  'SET grade = select.grade + 1, year = 2019 '
+                                  'WHEN select.grade > 233333333333'
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[9, 2019]]
+        self.check_result(resp, expected_result)
+
+    def test_update_vertex_failed(self):
+        # update vertex: the item is TagName.PropName = Expression in SET clause, 2.0 supported
+        resp = self.execute_query('UPDATE VERTEX ON course "101" '
+                                  'SET credits = credits + 1, name = "No9"')
+        self.check_resp_succeeded(resp)
+
+        # the $$.TagName.PropName expressions are not allowed in any update sentence
+        resp = self.execute_query('UPDATE VERTEX ON course "101" '
+                                  'SET credits = $$.course.credits + 1 '
+                                  'WHEN $$.course.name == "Math" && credits > $$.course.credits + 1 '
+                                  'YIELD name AS Name, credits AS Credits, $$.building.name')
+        self.check_resp_failed(resp)
+
+        # make sure TagName and PropertyName must exist in all clauses
+        resp = self.execute_query('UPDATE VERTEX ON nonexistentTag "101" '
+                                  'SET credits = credits + 1, name = "No9" '
+                                  'WHEN name == "Math" && nonexistentProperty > 2 '
+                                  'YIELD name AS Name, $^.nonexistentTag.nonexistentProperty')
+        self.check_resp_failed(resp)
+
+    def test_update_edge_failed(self):
+        # update edge: the item is PropName = Expression in SET clause
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@0'
+                                  'SET select = select.grade + 1, select.year = 2019')
+        self.check_resp_failed(resp)
+
+        # make sure EdgeName and PropertyName must exist in all clauses
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@0 '
+                                  'SET nonexistentProperty = select.grade + 1, year = 2019 '
+                                  'WHEN nonexistentEdgeName.grade > 4 && $^.student.nonexistentProperty > 15 '
+                                  'YIELD $^.nonexistentTag.name AS Name, select.nonexistentProperty AS Grade')
+        self.check_resp_failed(resp)
+
+        # make sure the edge_type must not exist
+        resp = self.execute_query('UPDATE EDGE ON nonexistentEdgeTypeName "200" -> "101"@0 '
+                                  'SET grade = select.grade + 1, year = 2019')
+        self.check_resp_failed(resp)
+
+
+    def test_upsert_vertex(self):
+        # vertex not exist
+        # resp = self.execute_query('FETCH PROP ON course "103" YIELD course.name, course.credits')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["103", "CS", 5]]
+        # self.check_result(resp, expected_result)
+
+        # not allow to handle multi tagid when update
+        resp = self.execute('UPDATE VERTEX ON course "103" '
+                            'SET credits = credits + 1, name = $^.building.name')
+        self.check_resp_failed(resp)
+
+        # update: vertex 103 ("CS", 5) --> ("CS", 6)
+        resp = self.execute_query('UPDATE VERTEX ON course "103" '
+                                  'SET credits = credits + 1 '
+                                  'WHEN name == "CS" && credits > 2 '
+                                  "YIELD name AS Name, credits AS Credits")
+        self.check_resp_succeeded(resp)
+        expected_result = [["CS", 6]]
+        self.check_result(resp, expected_result)
+
+        # when tag on vertex not exists, update failed
+        resp = self.execute_query('UPDATE VERTEX ON course "104" '
+                                  'SET credits = credits + 1  '
+                                  'WHEN name == "CS" && credits > 2 '
+                                  "YIELD name AS Name, credits AS Credits")
+        self.check_resp_failed(resp)
+
+        # has default value test,
+        # Insertable: vertex 110 ("Ann") --> ("Ann", "one"),
+        # 110 is nonexistent, gender with default value
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT VERTEX ON student_default "110" '
+        #                           'SET name = "Ann", age = 10 '
+        #                           "YIELD name AS Name, gender AS Gender")
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Ann", "one"]]
+        # self.check_result(resp, expected_result)
+
+        # Insertable failed, 111 is nonexistent, name and age without default value
+        resp = self.execute_query('UPSERT VERTEX ON student_default "111" '
+                                  'SET name = "Tom", '
+                                  'age = age + 8 '
+                                  "YIELD name AS Name, age AS Age")
+        self.check_resp_failed(resp)
+
+        # Insertable failed, 111 is nonexistent, name without default value
+        resp = self.execute_query('UPSERT VERTEX ON student_default "111" '
+                                  'SET gender = "two", age = 10 '
+                                  "YIELD name AS Name, gender AS Gender")
+        self.check_resp_failed(resp)
+
+        # Insertable: vertex 112 ("Lily") --> ("Lily", "one")
+        # 112 is nonexistent, gender with default value
+        # update student_default.age with string value
+        resp = self.execute_query('UPSERT VERTEX ON student_default "112" '
+                                  'SET name = "Lily", age = "10"'
+                                  "YIELD name AS Name, gender AS Gender")
+        self.check_resp_failed(resp)
+
+        # Insertable: vertex 113 ("Jack") --> ("Jack", "Three")
+        # 113 is nonexistent, gender with default value,
+        # update student_default.age with string value
+        resp = self.execute_query('UPSERT VERTEX ON student_default "113" '
+                                  'SET name = "Ann", age = "10"'
+                                  "YIELD name AS Name, gender AS Gender")
+        self.check_resp_failed(resp)
+
+        # when tag on vertex not exists, update failed
+        resp = self.execute_query('UPDATE VERTEX ON course "104" '
+                                  'SET credits = credits + 1 '
+                                  'WHEN name == \"CS\" && credits > 2 '
+                                  'YIELD name AS Name, credits AS Credits')
+        self.check_resp_failed(resp)
+
+        # Insertable success, 115 is nonexistent, name and age without default value,
+        # the filter is always true.
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT VERTEX ON student_default "115" '
+        #                           'SET name = "Kate", age = 12 '
+        #                           'WHEN gender == "two" '
+        #                           "YIELD name AS Name, age AS Age, gender AS gender")
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Kate", 12, "one"]]
+        # self.check_result(resp, expected_result)
+
+        # Order problem
+        # Insertable success, 116 is nonexistent, name and age without default value,
+        # the filter is always true.
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT VERTEX ON student_default "116" '
+        #                           'SET name = "Kate", age = birthday + 1,'
+        #                           'birthday = birthday + 1 '
+        #                           'WHEN gender == "two" '
+        #                           'YIELD name AS Name, age AS Age, '
+        #                           'gender AS gender, birthday AS birthday')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [['Kate', 2011, 'one', 2011]]
+        # self.check_result(resp, expected_result)
+
+        # Order problem
+        # Insertable success, 117 is nonexistent, name and age without default value,
+        # the filter is always true.
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT VERTEX ON student_default "117" '
+        #                           'SET birthday = birthday + 1, name = "Kate", age = birthday + 1 '
+        #                           'YIELD name AS Name, age AS Age, gender AS gender, birthday AS birthday')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["Kate", 2012, "one", 2011]]
+        # self.check_result(resp, expected_result)
+
+    def test_upsert_edge(self):
+        # resp = self.execute_query('FETCH PROP ON select "200"->"101"@0 YIELD select.grade, select.year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["200", "101", 0, 5, 2018]]
+        # self.check_result(resp, expected_result)
+
+        # Insertable, upsert when edge exists, 2.0 storage not support
+        resp = self.execute_query('UPSERT EDGE ON select "201" -> "101"@0'
+                                  'SET grade = 3, year = 2019 '
+                                  'WHEN $^.student.age > 15 && $^.student.gender == "male" '
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        # Insertable, upsert when edge not exist
+        resp = self.execute_query('UPSERT EDGE ON select "201" -> "101"@0'
+                                  'SET grade = 3, year = 2019 '
+                                  'WHEN select.grade > 3 '
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[3, 2019]]
+        self.check_result(resp, expected_result)
+
+        # update when edge not exists, failed
+        resp = self.execute_query('UPDATE EDGE ON select "601" -> "101"@0'
+                                  'SET year = 2019 '
+                                  'WHEN select.grade >10 '
+                                  'YIELD select.grade AS Grade, select.year AS Year')
+        self.check_resp_failed(resp)
+
+        # upsert when edge not exists,success
+        # filter condition is always true, insert default value or update value
+        resp = self.execute_query('UPSERT EDGE ON select "601" -> "101"@0'
+                                  'SET year = 2019, grade = 3 '
+                                  'WHEN grade >10 '
+                                  'YIELD grade AS Grade, year AS Year')
+        self.check_resp_succeeded(resp)
+        expected_result = [[3, 2019]]
+        self.check_result(resp, expected_result)
+
+        # resp = self.execute_query('FETCH PROP ON select 601->101@0 YIELD select.grade, select.year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["601", "101", 0, 3, 2019]]
+        # self.check_result(resp, expected_result)
+
+        # select_default's year with default value, timestamp not support
+        # resp = self.execute_query('UPSERT EDGE ON select_default "111" -> "222"@0 '
+        #                           'SET grade = 3 '
+        #                           'YIELD select_default.grade AS Grade, select_default.year AS Year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [[3, 1546308000]]
+        # self.check_result(resp, expected_result)
+
+        # select_default's year is timestamp type, set str type, , timestamp not support
+        # resp = self.execute_query('UPSERT EDGE ON select_default "222" -> "333"@0 '
+        #                           'SET grade = 3, year = "2020-01-10 10:00:00" '
+        #                           'YIELD select_default.grade AS Grade, select_default.year AS Year')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [[3, 1578621600]]
+        # self.check_result(resp, expected_result)
+
+        # select_default's grade without default value
+        resp = self.execute_query('UPSERT EDGE ON select_default "444" -> "555"@0 '
+                                  'SET year = 1546279201 '
+                                  'YIELD grade AS Grade, year AS Year')
+        self.check_resp_failed(resp)
+
+        # select_default's grade without default value
+        resp = self.execute_query('UPSERT EDGE ON select_default "333" -> "444"@0 '
+                                  'SET grade = 3 + grade '
+                                  'YIELD grade AS Grade, year AS Year')
+        self.check_resp_failed(resp)
+
+        # update select_default's year with edge prop value
+        # TODO: storage not ready
+        # resp = self.execute_query('UPSERT EDGE ON select_default "222" -> "444"@0 '
+        #                           'SET grade = 3, year = year + 10 '
+        #                           'YIELD grade AS Grade, year AS Year')
+        # self.check_resp_succeeded(resp)
+
+        # TODO: timestamp has not supported
+        # expected_result = [[3, 1546308010]]
+        # self.check_result(resp, expected_result)
+
+
+    def test_update_not_exist(self):
+        # make sure the vertex must not exist
+        resp = self.execute_query('UPDATE VERTEX ON course "1010000" '
+                                  'SET credits = credits + 1, name = "No9" '
+                                  'WHEN name == "Math" && credits > 2 '
+                                  'YIELD name as Name')
+        self.check_resp_failed(resp)
+
+        # make sure the edge(src, dst) must not exist
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101000000000000"@0  '
+                                  'SET grade = 1, year = 2019 ')
+        self.check_resp_failed(resp)
+
+        # make sure the edge(src, ranking, dst) must not exist
+        resp = self.execute_query('UPDATE EDGE ON select "200" -> "101"@123456789  '
+                                  'SET grade = grade + 1, year = 2019 ')
+        self.check_resp_failed(resp)
+
+
+    def test_upsert_then_insert(self):
+        resp = self.execute_query('UPSERT VERTEX ON building "100" SET name = "No1"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "100"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100", "No1"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('INSERT VERTEX building(name) VALUES "100": ("No2")')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "100"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100", "No2"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute_query('UPSERT VERTEX ON building "101" SET name = "No1"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "101"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["101", "No1"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('INSERT VERTEX building(name) VALUES "101": ("No2")')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "101"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["101", "No2"]]
+        # self.check_result(resp, expected_result)
+
+    def test_upsert_after_alter_schema(self):
+        resp = self.execute('INSERT VERTEX building(name) VALUES "100": ("No1")')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('ALTER TAG building ADD (new_field string default "123")')
+        self.check_resp_succeeded(resp)
+
+        time.sleep(self.delay)
+
+        resp = self.execute('UPSERT VERTEX ON building "100" SET name = "No2"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "100"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100", "No2", "123"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('UPSERT VERTEX ON building "100" SET name = "No3", new_field = "321"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "101"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100", "No3", "321"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('UPSERT VERTEX ON building "101" SET name = "No1", new_field = "No2"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP on building "101"')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["100",  "No1", "No2"]]
+        # self.check_result(resp, expected_result)
+
+        # Test upsert edge after alter schema
+        resp = self.execute('INSERT EDGE like(likeness) VALUES "1" -> "100":(1.0)')
+        self.check_resp_succeeded(resp)
+
+        resp = self.execute('ALTER EDGE like ADD (new_field string default "123")')
+        self.check_resp_succeeded(resp)
+
+        time.sleep(self.delay)
+
+        resp = self.execute('UPSERT EDGE ON like "1"->"100" SET likeness = 2.0')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP ON like "1"->"100"@0')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["1", "100", 0, 2.0, "123"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('UPSERT EDGE ON like "1"->"100" SET likeness = 3.0, new_field = "321"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP ON like "1"->"100"@0')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["1", "100", 0, 3.0, "321"]]
+        # self.check_result(resp, expected_result)
+
+        resp = self.execute('UPSERT EDGE ON like "1"->"101" SET likeness = 1.0, new_field = "111"')
+        self.check_resp_succeeded(resp)
+
+        # resp = self.execute_query('FETCH PROP ON like "1"->"101"@0')
+        # self.check_resp_succeeded(resp)
+        # expected_result = [["1", "101", 0, 1.0, "111"]]
+        # self.check_result(resp, expected_result)
+