@Override
    protected Scope visitUpdate(final Update node, final Optional<Scope> context) {
      queryContext.setQueryType(QueryType.WRITE);
      node.parseTable(sessionContext);
      accessControl.checkCanInsertIntoTable(
          sessionContext.getUserName(),
          new QualifiedObjectName(node.getDatabase(), node.getTableName()));
      final TranslationMap translationMap = analyzeTraverseDevice(node, context, true);
      final TsTable table =
          DataNodeTableCache.getInstance().getTable(node.getDatabase(), node.getTableName());
      DataNodeTreeViewSchemaUtils.checkTableInWrite(node.getDatabase(), table);
      if (!node.parseRawExpression(
          null,
          table,
          table.getColumnList().stream()
              .filter(
                  columnSchema ->
                      columnSchema.getColumnCategory().equals(TsTableColumnCategory.ATTRIBUTE))
              .map(TsTableColumnSchema::getColumnName)
              .collect(Collectors.toList()),
          queryContext)) {
        analysis.setFinishQueryAfterAnalyze();
        return null;
      }

      // If node.location is absent, this is a pipe-transferred update, namely the assignments are
      // already parsed at the sender
      if (node.getLocation().isPresent()) {
        final Set<SymbolReference> attributeNames = new HashSet<>();
        node.setAssignments(
            node.getAssignments().stream()
                .map(
                    assignment -> {
                      final Expression parsedColumn =
                          analyzeAndRewriteExpression(
                              translationMap, translationMap.getScope(), assignment.getName());
                      if (!(parsedColumn instanceof SymbolReference)
                          || table
                                  .getColumnSchema(((SymbolReference) parsedColumn).getName())
                                  .getColumnCategory()
                              != TsTableColumnCategory.ATTRIBUTE) {
                        throw new SemanticException("Update can only specify attribute columns.");
                      }
                      if (attributeNames.contains(parsedColumn)) {
                        throw new SemanticException(
                            "Update attribute shall specify a attribute only once.");
                      }
                      attributeNames.add((SymbolReference) parsedColumn);

                      return new UpdateAssignment(
                          parsedColumn,
                          analyzeAndRewriteExpression(
                              translationMap, translationMap.getScope(), assignment.getValue()));
                    })
                .collect(Collectors.toList()));
      }
      return null;
    }
Visitor context represents local query scope (if exists). The invariant is that the local query
scopes hierarchy should always have outer query scope (if provided) as ancestor.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java