This is an automated email from the ASF dual-hosted git repository.

shuwenwei pushed a commit to branch calc_commons
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/calc_commons by this push:
     new 9cc8309468c move partial sql formatter
9cc8309468c is described below

commit 9cc8309468c9291b64ef308d7ec47160c6a3a4cf
Author: shuwenwei <[email protected]>
AuthorDate: Thu Apr 16 16:05:21 2026 +0800

    move partial sql formatter
---
 .../table/v1/handler/ExceptionHandler.java         |    2 +-
 .../sql/util/CommonQuerySqlFormatter.java          |  346 ++++
 .../relational/sql/util/DataNodeSqlFormatter.java  | 1414 ++++++++++++++++
 .../plan/relational/sql/util/SqlFormatter.java     | 1723 +-------------------
 4 files changed, 1777 insertions(+), 1708 deletions(-)

diff --git 
a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/handler/ExceptionHandler.java
 
b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/handler/ExceptionHandler.java
index 61e64465a06..a072e4d2c16 100644
--- 
a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/handler/ExceptionHandler.java
+++ 
b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/table/v1/handler/ExceptionHandler.java
@@ -27,7 +27,7 @@ import 
org.apache.iotdb.db.exception.metadata.DatabaseNotSetException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
 import org.apache.iotdb.db.exception.sql.SemanticException;
 import org.apache.iotdb.db.exception.sql.StatementAnalyzeException;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.parser.ParsingException;
+import 
org.apache.iotdb.db.node_commons.plan.relational.sql.parser.ParsingException;
 import org.apache.iotdb.rest.protocol.model.ExecutionStatus;
 import org.apache.iotdb.rpc.TSStatusCode;
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java
new file mode 100644
index 00000000000..762200bf1a3
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.plan.relational.sql.util;
+
+import 
org.apache.iotdb.db.node_commons.plan.relational.sql.ast.AliasedRelation;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.AllColumns;
+import 
org.apache.iotdb.db.node_commons.plan.relational.sql.ast.CommonQueryAstVisitor;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Expression;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Fill;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Identifier;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Join;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.JoinCriteria;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Limit;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Node;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Offset;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.OrderBy;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Query;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Relation;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Row;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.SingleColumn;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.TableSubquery;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Values;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.WithQuery;
+import org.apache.iotdb.db.node_commons.plan.statement.component.FillPolicy;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.JoinOn;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.JoinUsing;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.NaturalJoin;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+
+import java.util.Iterator;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static java.util.stream.Collectors.joining;
+import static 
org.apache.iotdb.db.node_commons.plan.relational.sql.util.ExpressionFormatter.formatOrderBy;
+
+public class CommonQuerySqlFormatter implements CommonQueryAstVisitor<Void, 
Integer> {
+
+  protected final SqlFormatter.SqlBuilder builder;
+
+  public CommonQuerySqlFormatter(StringBuilder builder) {
+    this.builder = new SqlFormatter.SqlBuilder(builder);
+  }
+
+  @Override
+  public Void visitNode(Node node, Integer indent) {
+    throw new UnsupportedOperationException("not yet implemented: " + node);
+  }
+
+  @Override
+  public Void visitExpression(Expression node, Integer indent) {
+    checkArgument(indent == 0, "visitExpression should only be called at 
root");
+    builder.append(SqlFormatter.formatExpression(node));
+    return null;
+  }
+
+  @Override
+  public Void visitQuery(Query node, Integer indent) {
+    node.getWith()
+        .ifPresent(
+            with -> {
+              append(indent, "WITH");
+              if (with.isRecursive()) {
+                builder.append(" RECURSIVE");
+              }
+              builder.append("\n  ");
+              Iterator<WithQuery> queries = with.getQueries().iterator();
+              while (queries.hasNext()) {
+                WithQuery query = queries.next();
+                append(indent, SqlFormatter.formatName(query.getName()));
+                query
+                    .getColumnNames()
+                    .ifPresent(columnNames -> appendAliasColumns(builder, 
columnNames));
+                builder.append(" AS ");
+                process(new TableSubquery(query.getQuery()), indent);
+                builder.append('\n');
+                if (queries.hasNext()) {
+                  builder.append(", ");
+                }
+              }
+            });
+
+    processRelation(node.getQueryBody(), indent);
+    node.getOrderBy().ifPresent(orderBy -> process(orderBy, indent));
+    node.getOffset().ifPresent(offset -> process(offset, indent));
+    node.getLimit().ifPresent(limit -> process(limit, indent));
+    return null;
+  }
+
+  @Override
+  public Void visitFill(Fill node, Integer indent) {
+    append(indent, "FILL METHOD ").append(node.getFillMethod().name());
+
+    if (node.getFillMethod() == FillPolicy.CONSTANT) {
+      builder.append(SqlFormatter.formatExpression(node.getFillValue().get()));
+    } else if (node.getFillMethod() == FillPolicy.LINEAR) {
+      node.getTimeColumnIndex()
+          .ifPresent(index -> builder.append(" TIME_COLUMN 
").append(String.valueOf(index)));
+      node.getFillGroupingElements()
+          .ifPresent(
+              elements ->
+                  builder
+                      .append(" FILL_GROUP ")
+                      .append(
+                          elements.stream()
+                              .map(SqlFormatter::formatExpression)
+                              .collect(joining(", "))));
+    } else if (node.getFillMethod() == FillPolicy.PREVIOUS) {
+      node.getTimeBound()
+          .ifPresent(timeBound -> builder.append(" TIME_BOUND 
").append(timeBound.toString()));
+      node.getTimeColumnIndex()
+          .ifPresent(index -> builder.append(" TIME_COLUMN 
").append(String.valueOf(index)));
+      node.getFillGroupingElements()
+          .ifPresent(
+              elements ->
+                  builder
+                      .append(" FILL_GROUP ")
+                      .append(
+                          elements.stream()
+                              .map(SqlFormatter::formatExpression)
+                              .collect(joining(", "))));
+    } else {
+      throw new IllegalArgumentException("Unknown fill method: " + 
node.getFillMethod());
+    }
+    return null;
+  }
+
+  @Override
+  public Void visitOrderBy(OrderBy node, Integer indent) {
+    append(indent, formatOrderBy(node)).append('\n');
+    return null;
+  }
+
+  @Override
+  public Void visitOffset(Offset node, Integer indent) {
+    append(indent, "OFFSET ")
+        .append(SqlFormatter.formatExpression(node.getRowCount()))
+        .append(" ROWS\n");
+    return null;
+  }
+
+  @Override
+  public Void visitLimit(Limit node, Integer indent) {
+    append(indent, "LIMIT 
").append(SqlFormatter.formatExpression(node.getRowCount())).append('\n');
+    return null;
+  }
+
+  @Override
+  public Void visitSingleColumn(SingleColumn node, Integer indent) {
+    builder.append(SqlFormatter.formatExpression(node.getExpression()));
+    node.getAlias().ifPresent(alias -> builder.append(' 
').append(SqlFormatter.formatName(alias)));
+    return null;
+  }
+
+  @Override
+  public Void visitAllColumns(AllColumns node, Integer indent) {
+    node.getTarget()
+        .ifPresent(value -> 
builder.append(SqlFormatter.formatExpression(value)).append("."));
+    builder.append("*");
+
+    if (!node.getAliases().isEmpty()) {
+      builder
+          .append(" AS (")
+          .append(
+              Joiner.on(", ")
+                  .join(
+                      node.getAliases().stream()
+                          .map(SqlFormatter::formatName)
+                          .collect(toImmutableList())))
+          .append(")");
+    }
+
+    return null;
+  }
+
+  @Override
+  public Void visitJoin(Join node, Integer indent) {
+    JoinCriteria criteria = node.getCriteria().orElse(null);
+    String type = node.getType().toString();
+    if (criteria instanceof NaturalJoin) {
+      type = "NATURAL " + type;
+    }
+
+    if (node.getType() != Join.Type.IMPLICIT) {
+      builder.append('(');
+    }
+    process(node.getLeft(), indent);
+
+    builder.append('\n');
+    if (node.getType() == Join.Type.IMPLICIT) {
+      append(indent, ", ");
+    } else {
+      append(indent, type).append(" JOIN ");
+    }
+
+    process(node.getRight(), indent);
+
+    if (node.getType() != Join.Type.CROSS && node.getType() != 
Join.Type.IMPLICIT) {
+      if (criteria instanceof JoinUsing) {
+        JoinUsing using = (JoinUsing) criteria;
+        builder.append(" USING (").append(Joiner.on(", 
").join(using.getColumns())).append(")");
+      } else if (criteria instanceof JoinOn) {
+        JoinOn on = (JoinOn) criteria;
+        builder.append(" ON 
").append(SqlFormatter.formatExpression(on.getExpression()));
+      } else if (!(criteria instanceof NaturalJoin)) {
+        throw new UnsupportedOperationException("unknown join criteria: " + 
criteria);
+      }
+    }
+
+    if (node.getType() != Join.Type.IMPLICIT) {
+      builder.append(")");
+    }
+
+    return null;
+  }
+
+  @Override
+  public Void visitAliasedRelation(AliasedRelation node, Integer indent) {
+    processRelationSuffix(node.getRelation(), indent);
+
+    builder.append(' ').append(SqlFormatter.formatName(node.getAlias()));
+    appendAliasColumns(builder, node.getColumnNames());
+
+    return null;
+  }
+
+  @Override
+  public Void visitValues(Values node, Integer indent) {
+    builder.append(" VALUES ");
+
+    boolean first = true;
+    for (Expression row : node.getRows()) {
+      builder.append("\n").append(indentString(indent)).append(first ? "  " : 
", ");
+      builder.append(SqlFormatter.formatExpression(row));
+      first = false;
+    }
+    builder.append('\n');
+
+    return null;
+  }
+
+  @Override
+  public Void visitTableSubquery(TableSubquery node, Integer indent) {
+    builder.append('(').append('\n');
+
+    process(node.getQuery(), indent + 1);
+
+    append(indent, ") ");
+
+    return null;
+  }
+
+  @Override
+  public Void visitRow(Row node, Integer indent) {
+    builder.append("ROW(");
+    boolean firstItem = true;
+    for (Expression item : node.getItems()) {
+      if (!firstItem) {
+        builder.append(", ");
+      }
+      process(item, indent);
+      firstItem = false;
+    }
+    builder.append(")");
+    return null;
+  }
+
+  protected void processRelationSuffix(Relation relation, Integer indent) {
+    if (needsParenthesesForRelationSuffix(relation)) {
+      builder.append("( ");
+      process(relation, indent + 1);
+      append(indent, ")");
+    } else {
+      process(relation, indent);
+    }
+  }
+
+  protected boolean needsParenthesesForRelationSuffix(Relation relation) {
+    return relation instanceof AliasedRelation;
+  }
+
+  protected void processRelation(Relation relation, Integer indent) {
+    if (isSimpleTableRelation(relation)) {
+      builder.append("TABLE 
").append(formatSimpleTableRelationName(relation)).append('\n');
+    } else {
+      process(relation, indent);
+    }
+  }
+
+  protected boolean isSimpleTableRelation(Relation relation) {
+    return false;
+  }
+
+  protected String formatSimpleTableRelationName(Relation relation) {
+    throw new UnsupportedOperationException("unsupported relation: " + 
relation);
+  }
+
+  protected SqlFormatter.SqlBuilder append(int indent, String value) {
+    return builder.append(indentString(indent)).append(value);
+  }
+
+  protected static String indentString(int indent) {
+    return Strings.repeat(SqlFormatter.INDENT, indent);
+  }
+
+  protected void formatDefinitionList(List<String> elements, int indent) {
+    if (elements.size() == 1) {
+      builder.append(" ").append(getOnlyElement(elements)).append("\n");
+    } else {
+      builder.append("\n");
+      for (int i = 0; i < elements.size() - 1; i++) {
+        append(indent, elements.get(i)).append(",\n");
+      }
+      append(indent, elements.get(elements.size() - 1)).append("\n");
+    }
+  }
+
+  protected void appendAliasColumns(SqlFormatter.SqlBuilder builder, 
List<Identifier> columns) {
+    if ((columns != null) && !columns.isEmpty()) {
+      String formattedColumns =
+          columns.stream().map(SqlFormatter::formatName).collect(joining(", 
"));
+      builder.append(" (").append(formattedColumns).append(')');
+    }
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java
new file mode 100644
index 00000000000..a57b5893510
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java
@@ -0,0 +1,1414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.plan.relational.sql.util;
+
+import 
org.apache.iotdb.db.node_commons.plan.relational.sql.ast.AliasedRelation;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Expression;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Identifier;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Node;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Relation;
+import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.TableSubquery;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AddColumn;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterDB;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterPipe;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ColumnDefinition;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CopyTo;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipe;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipePlugin;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTable;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTopic;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateView;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Delete;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipe;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipePlugin;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropSubscription;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropTable;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropTopic;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Except;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Explain;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExplainAnalyze;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Insert;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Intersect;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.LoadTsFile;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.PatternRecognitionRelation;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Property;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.QuerySpecification;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameColumn;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameTable;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RowPattern;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Select;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetColumnComment;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetProperties;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetTableComment;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowClusterId;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentDatabase;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentSqlDialect;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentTimestamp;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentUser;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowFunctions;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowPipePlugins;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowPipes;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowSubscriptions;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowTables;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowTopics;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowVariables;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowVersion;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartPipe;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopPipe;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Table;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TableFunctionArgument;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TableFunctionInvocation;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TableFunctionTableArgument;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Union;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Update;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.UpdateAssignment;
+
+import com.google.common.base.Joiner;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.stream.Collectors.joining;
+import static 
org.apache.iotdb.db.node_commons.plan.relational.sql.util.ExpressionFormatter.formatOrderBy;
+import static 
org.apache.iotdb.db.queryengine.plan.relational.sql.util.RowPatternFormatter.formatPattern;
+
+public class DataNodeSqlFormatter extends CommonQuerySqlFormatter
+    implements AstVisitor<Void, Integer> {
+
+  public DataNodeSqlFormatter(StringBuilder builder) {
+    super(builder);
+  }
+
+  @Override
+  public Void visitRowPattern(RowPattern node, Integer indent) {
+    checkArgument(indent == 0, "visitRowPattern should only be called at 
root");
+    builder.append(formatPattern(node));
+    return null;
+  }
+
+  @Override
+  public Void visitQuerySpecification(QuerySpecification node, Integer indent) 
{
+    process(node.getSelect(), indent);
+
+    node.getFrom()
+        .ifPresent(
+            from -> {
+              append(indent, "FROM");
+              builder.append('\n');
+              append(indent, "  ");
+              process(from, indent);
+            });
+
+    builder.append('\n');
+
+    node.getWhere()
+        .ifPresent(
+            where -> append(indent, "WHERE " + 
SqlFormatter.formatExpression(where)).append('\n'));
+
+    node.getGroupBy()
+        .ifPresent(
+            groupBy ->
+                append(
+                        indent,
+                        "GROUP BY "
+                            + (groupBy.isDistinct() ? " DISTINCT " : "")
+                            + 
org.apache.iotdb.db.node_commons.plan.relational.sql.util
+                                
.ExpressionFormatter.formatGroupBy(groupBy.getGroupingElements()))
+                    .append('\n'));
+
+    node.getHaving()
+        .ifPresent(
+            having ->
+                append(indent, "HAVING " + 
SqlFormatter.formatExpression(having)).append('\n'));
+
+    node.getOrderBy().ifPresent(orderBy -> process(orderBy, indent));
+    node.getOffset().ifPresent(offset -> process(offset, indent));
+    node.getLimit().ifPresent(limit -> process(limit, indent));
+    return null;
+  }
+
+  @Override
+  public Void visitSelect(Select node, Integer indent) {
+    append(indent, "SELECT");
+    if (node.isDistinct()) {
+      builder.append(" DISTINCT");
+    }
+
+    if (node.getSelectItems().size() > 1) {
+      boolean first = true;
+      for (org.apache.iotdb.db.node_commons.plan.relational.sql.ast.SelectItem 
item :
+          node.getSelectItems()) {
+        builder.append("\n").append(indentString(indent)).append(first ? "  " 
: ", ");
+        process(item, indent);
+        first = false;
+      }
+    } else {
+      builder.append(' ');
+      
process(com.google.common.collect.Iterables.getOnlyElement(node.getSelectItems()),
 indent);
+    }
+
+    builder.append('\n');
+
+    return null;
+  }
+
+  @Override
+  public Void visitTable(Table node, Integer indent) {
+    builder.append(SqlFormatter.formatName(node.getName()));
+    return null;
+  }
+
+  @Override
+  public Void visitPatternRecognitionRelation(PatternRecognitionRelation node, 
Integer indent) {
+    processRelationSuffix(node.getInput(), indent);
+
+    builder.append(" MATCH_RECOGNIZE (\n");
+    if (!node.getPartitionBy().isEmpty()) {
+      append(indent + 1, "PARTITION BY ")
+          .append(
+              node.getPartitionBy().stream()
+                  .map(
+                      
org.apache.iotdb.db.node_commons.plan.relational.sql.util.ExpressionFormatter
+                          ::formatExpression)
+                  .collect(joining(", ")))
+          .append("\n");
+    }
+    if (node.getOrderBy().isPresent()) {
+      process(node.getOrderBy().get(), indent + 1);
+    }
+    if (!node.getMeasures().isEmpty()) {
+      append(indent + 1, "MEASURES");
+      formatDefinitionList(
+          node.getMeasures().stream()
+              .map(
+                  measure ->
+                      SqlFormatter.formatExpression(measure.getExpression())
+                          + " AS "
+                          + SqlFormatter.formatExpression(measure.getName()))
+              
.collect(com.google.common.collect.ImmutableList.toImmutableList()),
+          indent + 2);
+    }
+    if (node.getRowsPerMatch().isPresent()) {
+      String rowsPerMatch;
+      switch (node.getRowsPerMatch().get()) {
+        case ONE:
+          rowsPerMatch = "ONE ROW PER MATCH";
+          break;
+        case ALL_SHOW_EMPTY:
+          rowsPerMatch = "ALL ROWS PER MATCH SHOW EMPTY MATCHES";
+          break;
+        case ALL_OMIT_EMPTY:
+          rowsPerMatch = "ALL ROWS PER MATCH OMIT EMPTY MATCHES";
+          break;
+        case ALL_WITH_UNMATCHED:
+          rowsPerMatch = "ALL ROWS PER MATCH WITH UNMATCHED ROWS";
+          break;
+        default:
+          throw new IllegalStateException(
+              "unexpected rowsPerMatch: " + node.getRowsPerMatch().get());
+      }
+      append(indent + 1, rowsPerMatch).append("\n");
+    }
+    if (node.getAfterMatchSkipTo().isPresent()) {
+      String skipTo;
+      switch (node.getAfterMatchSkipTo().get().getPosition()) {
+        case PAST_LAST:
+          skipTo = "AFTER MATCH SKIP PAST LAST ROW";
+          break;
+        case NEXT:
+          skipTo = "AFTER MATCH SKIP TO NEXT ROW";
+          break;
+        case LAST:
+          checkState(
+              node.getAfterMatchSkipTo().get().getIdentifier().isPresent(),
+              "missing identifier in AFTER MATCH SKIP TO LAST");
+          skipTo =
+              "AFTER MATCH SKIP TO LAST "
+                  + SqlFormatter.formatExpression(
+                      node.getAfterMatchSkipTo().get().getIdentifier().get());
+          break;
+        case FIRST:
+          checkState(
+              node.getAfterMatchSkipTo().get().getIdentifier().isPresent(),
+              "missing identifier in AFTER MATCH SKIP TO FIRST");
+          skipTo =
+              "AFTER MATCH SKIP TO FIRST "
+                  + SqlFormatter.formatExpression(
+                      node.getAfterMatchSkipTo().get().getIdentifier().get());
+          break;
+        default:
+          throw new IllegalStateException("unexpected skipTo: " + 
node.getAfterMatchSkipTo().get());
+      }
+      append(indent + 1, skipTo).append("\n");
+    }
+    append(indent + 1, "PATTERN 
(").append(formatPattern(node.getPattern())).append(")\n");
+    if (!node.getSubsets().isEmpty()) {
+      append(indent + 1, "SUBSET");
+      formatDefinitionList(
+          node.getSubsets().stream()
+              .map(
+                  subset ->
+                      SqlFormatter.formatExpression(subset.getName())
+                          + " = "
+                          + subset.getIdentifiers().stream()
+                              .map(
+                                  
org.apache.iotdb.db.node_commons.plan.relational.sql.util
+                                          .ExpressionFormatter
+                                      ::formatExpression)
+                              .collect(joining(", ", "(", ")")))
+              
.collect(com.google.common.collect.ImmutableList.toImmutableList()),
+          indent + 2);
+    }
+    append(indent + 1, "DEFINE");
+    formatDefinitionList(
+        node.getVariableDefinitions().stream()
+            .map(
+                variable ->
+                    SqlFormatter.formatExpression(variable.getName())
+                        + " AS "
+                        + 
SqlFormatter.formatExpression(variable.getExpression()))
+            
.collect(com.google.common.collect.ImmutableList.toImmutableList()),
+        indent + 2);
+
+    builder.append(")");
+
+    return null;
+  }
+
+  @Override
+  public Void visitUnion(Union node, Integer indent) {
+    Iterator<Relation> relations = node.getRelations().iterator();
+
+    while (relations.hasNext()) {
+      processRelation(relations.next(), indent);
+
+      if (relations.hasNext()) {
+        builder.append("UNION ");
+        if (!node.isDistinct()) {
+          builder.append("ALL ");
+        }
+      }
+    }
+
+    return null;
+  }
+
+  @Override
+  public Void visitExcept(Except node, Integer indent) {
+    processRelation(node.getLeft(), indent);
+
+    builder.append("EXCEPT ");
+    if (!node.isDistinct()) {
+      builder.append("ALL ");
+    }
+
+    processRelation(node.getRight(), indent);
+
+    return null;
+  }
+
+  @Override
+  public Void visitIntersect(Intersect node, Integer indent) {
+    Iterator<Relation> relations = node.getRelations().iterator();
+
+    while (relations.hasNext()) {
+      processRelation(relations.next(), indent);
+
+      if (relations.hasNext()) {
+        builder.append("INTERSECT ");
+        if (!node.isDistinct()) {
+          builder.append("ALL ");
+        }
+      }
+    }
+
+    return null;
+  }
+
+  @Override
+  public Void visitExplain(Explain node, Integer indent) {
+    builder.append("EXPLAIN ");
+    builder.append("\n");
+    process(node.getStatement(), indent);
+    return null;
+  }
+
+  @Override
+  public Void visitCopyTo(CopyTo node, Integer context) {
+    builder.append("COPY\n");
+    builder.append("(\n");
+    process(node.getQueryStatement(), context);
+    builder.append("\n) ");
+    builder.append("TO ");
+    builder.append('\'');
+    builder.append(node.getTargetFileName());
+    builder.append('\'');
+    builder.append("\n");
+    builder.append(node.getOptions().toString());
+    return null;
+  }
+
+  @Override
+  public Void visitExplainAnalyze(ExplainAnalyze node, Integer indent) {
+    builder.append("EXPLAIN ANALYZE");
+    if (node.isVerbose()) {
+      builder.append(" VERBOSE");
+    }
+    builder.append("\n");
+    process(node.getStatement(), indent);
+    return null;
+  }
+
+  @Override
+  public Void visitShowDB(ShowDB node, Integer indent) {
+    builder.append("SHOW DATABASE");
+    return null;
+  }
+
+  @Override
+  public Void visitShowTables(ShowTables node, Integer indent) {
+    builder.append("SHOW TABLES");
+
+    if (node.isDetails()) {
+      builder.append(" DETAILS");
+    }
+
+    node.getDbName().ifPresent(db -> builder.append(" FROM 
").append(SqlFormatter.formatName(db)));
+
+    return null;
+  }
+
+  @Override
+  public Void visitShowFunctions(ShowFunctions node, Integer indent) {
+    builder.append("SHOW FUNCTIONS");
+    return null;
+  }
+
+  @Override
+  public Void visitShowCurrentSqlDialect(ShowCurrentSqlDialect node, Integer 
context) {
+    builder.append(node.toString());
+    return null;
+  }
+
+  @Override
+  public Void visitShowCurrentDatabase(ShowCurrentDatabase node, Integer 
context) {
+    builder.append(node.toString());
+    return null;
+  }
+
+  @Override
+  public Void visitShowCurrentUser(ShowCurrentUser node, Integer context) {
+    builder.append(node.toString());
+    return null;
+  }
+
+  @Override
+  public Void visitShowVersion(ShowVersion node, Integer context) {
+    builder.append(node.toString());
+    return null;
+  }
+
+  @Override
+  public Void visitShowVariables(ShowVariables node, Integer context) {
+    builder.append(node.toString());
+    return null;
+  }
+
+  @Override
+  public Void visitShowClusterId(ShowClusterId node, Integer context) {
+    builder.append(node.toString());
+    return null;
+  }
+
+  @Override
+  public Void visitShowCurrentTimestamp(ShowCurrentTimestamp node, Integer 
context) {
+    builder.append(node.toString());
+    return null;
+  }
+
+  @Override
+  public Void visitDelete(Delete node, Integer indent) {
+    builder.append("DELETE FROM 
").append(SqlFormatter.formatName(node.getTable().getName()));
+    node.getWhere()
+        .ifPresent(where -> builder.append(" WHERE 
").append(SqlFormatter.formatExpression(where)));
+    return null;
+  }
+
+  @Override
+  public Void visitCreateDB(CreateDB node, Integer indent) {
+    builder.append("CREATE DATABASE ");
+    if (node.exists()) {
+      builder.append("IF NOT EXISTS ");
+    }
+    builder.append(node.getDbName()).append(" ");
+    builder.append(formatPropertiesMultiLine(node.getProperties()));
+    return null;
+  }
+
+  @Override
+  public Void visitAlterDB(AlterDB node, Integer indent) {
+    builder.append("ALTER DATABASE ");
+    if (node.exists()) {
+      builder.append("IF EXISTS ");
+    }
+    builder.append(node.getDbName()).append(" ");
+    builder.append(formatPropertiesMultiLine(node.getProperties()));
+    return null;
+  }
+
+  @Override
+  public Void visitDropDB(DropDB node, Integer indent) {
+    builder.append("DROP DATABASE ");
+    if (node.isExists()) {
+      builder.append("IF EXISTS ");
+    }
+    builder.append(SqlFormatter.formatName(node.getDbName()));
+    return null;
+  }
+
+  @Override
+  public Void visitCreateTable(CreateTable node, Integer indent) {
+    builder.append("CREATE TABLE ");
+    if (node.isIfNotExists()) {
+      builder.append("IF NOT EXISTS ");
+    }
+    String tableName = SqlFormatter.formatName(node.getName());
+    builder.append(tableName).append(" (\n");
+
+    String elementIndent = indentString(indent + 1);
+    String columnList =
+        node.getElements().stream()
+            .map(
+                element -> {
+                  if (element != null) {
+                    return elementIndent + formatColumnDefinition(element);
+                  }
+                  throw new UnsupportedOperationException("unknown table 
element: " + element);
+                })
+            .collect(joining(",\n"));
+    builder.append(columnList);
+    builder.append("\n").append(")");
+
+    node.getCharsetName().ifPresent(charset -> builder.append(" CHARSET 
").append(charset));
+
+    if (Objects.nonNull(node.getComment())) {
+      builder.append(" COMMENT '").append(node.getComment()).append("'");
+    }
+
+    builder.append(formatPropertiesMultiLine(node.getProperties()));
+    return null;
+  }
+
+  @Override
+  public Void visitCreateView(CreateView node, Integer indent) {
+    builder.append("CREATE ");
+    if (node.isReplace()) {
+      builder.append("OR REPLACE ");
+    }
+    builder.append("VIEW ");
+    String tableName = SqlFormatter.formatName(node.getName());
+    builder.append(tableName).append(" (\n");
+
+    String elementIndent = indentString(indent + 1);
+    String columnList =
+        node.getElements().stream()
+            .map(
+                element -> {
+                  if (element != null) {
+                    return elementIndent + formatColumnDefinition(element);
+                  }
+                  throw new UnsupportedOperationException("unknown table 
element: " + element);
+                })
+            .collect(joining(",\n"));
+    builder.append(columnList);
+    builder.append("\n").append(")");
+
+    if (Objects.nonNull(node.getComment())) {
+      builder.append(" COMMENT '").append(node.getComment()).append("'");
+    }
+
+    if (node.isRestrict()) {
+      builder.append(" RESTRICT");
+    }
+
+    builder.append(formatPropertiesMultiLine(node.getProperties()));
+    builder.append(" AS ").append(node.getPrefixPath().toString());
+
+    return null;
+  }
+
+  @Override
+  public Void visitDropTable(DropTable node, Integer indent) {
+    builder.append("DROP");
+    builder.append(node.isView() ? " VIEW " : " TABLE ");
+    if (node.isExists()) {
+      builder.append("IF EXISTS ");
+    }
+    builder.append(SqlFormatter.formatName(node.getTableName()));
+
+    return null;
+  }
+
+  @Override
+  public Void visitRenameTable(RenameTable node, Integer indent) {
+    builder.append("ALTER");
+    builder.append(node.isView() ? " VIEW " : " TABLE ");
+    if (node.tableIfExists()) {
+      builder.append("IF EXISTS ");
+    }
+
+    builder
+        .append(SqlFormatter.formatName(node.getSource()))
+        .append(" RENAME TO ")
+        .append(SqlFormatter.formatName(node.getTarget()));
+
+    return null;
+  }
+
+  @Override
+  public Void visitSetProperties(SetProperties node, Integer context) {
+    SetProperties.Type type = node.getType();
+    builder.append("ALTER ");
+    switch (type) {
+      case TABLE:
+        builder.append("TABLE ");
+      case MATERIALIZED_VIEW:
+        builder.append("MATERIALIZED VIEW ");
+      case TREE_VIEW:
+        builder.append("VIEW ");
+    }
+    if (node.ifExists()) {
+      builder.append("IF EXISTS ");
+    }
+
+    builder
+        .append(SqlFormatter.formatName(node.getName()))
+        .append(" SET PROPERTIES ")
+        .append(joinProperties(node.getProperties()));
+
+    return null;
+  }
+
+  @Override
+  public Void visitRenameColumn(RenameColumn node, Integer indent) {
+    builder.append("ALTER");
+    builder.append(node.isView() ? " VIEW " : " TABLE ");
+    if (node.tableIfExists()) {
+      builder.append("IF EXISTS ");
+    }
+
+    builder.append(SqlFormatter.formatName(node.getTable())).append("RENAME 
COLUMN ");
+    if (node.columnIfExists()) {
+      builder.append("IF EXISTS ");
+    }
+
+    builder
+        .append(SqlFormatter.formatName(node.getSource()))
+        .append(" TO ")
+        .append(SqlFormatter.formatName(node.getTarget()));
+
+    return null;
+  }
+
+  @Override
+  public Void visitDropColumn(DropColumn node, Integer indent) {
+    builder.append("ALTER");
+    builder.append(node.isView() ? " VIEW " : " TABLE ");
+    if (node.tableIfExists()) {
+      builder.append("IF EXISTS ");
+    }
+
+    builder.append(SqlFormatter.formatName(node.getTable())).append("DROP 
COLUMN ");
+    if (node.columnIfExists()) {
+      builder.append("IF EXISTS ");
+    }
+
+    builder.append(SqlFormatter.formatName(node.getField()));
+
+    return null;
+  }
+
+  @Override
+  public Void visitAddColumn(AddColumn node, Integer indent) {
+    builder.append("ALTER");
+    builder.append(node.isView() ? " VIEW " : " TABLE ");
+    if (node.tableIfExists()) {
+      builder.append("IF EXISTS ");
+    }
+
+    builder.append(SqlFormatter.formatName(node.getTableName())).append("ADD 
COLUMN ");
+    if (node.columnIfNotExists()) {
+      builder.append("IF NOT EXISTS ");
+    }
+
+    builder.append(formatColumnDefinition(node.getColumn()));
+
+    return null;
+  }
+
+  @Override
+  public Void visitSetTableComment(SetTableComment node, Integer indent) {
+    builder
+        .append("COMMENT ON")
+        .append(node.isView() ? " VIEW " : " TABLE ")
+        .append(SqlFormatter.formatName(node.getTableName()))
+        .append(" IS ")
+        .append(node.getComment());
+
+    return null;
+  }
+
+  @Override
+  public Void visitSetColumnComment(SetColumnComment node, Integer indent) {
+    builder
+        .append("COMMENT ON COLUMN ")
+        .append(SqlFormatter.formatName(node.getTable()))
+        .append(".")
+        .append(SqlFormatter.formatName(node.getField()))
+        .append(" IS ")
+        .append(node.getComment());
+    return null;
+  }
+
+  @Override
+  public Void visitInsert(Insert node, Integer indent) {
+    builder.append("INSERT INTO 
").append(SqlFormatter.formatName(node.getTarget()));
+
+    node.getColumns()
+        .ifPresent(
+            columns -> builder.append(" (").append(Joiner.on(", 
").join(columns)).append(")"));
+
+    builder.append("\n");
+
+    process(node.getQuery(), indent);
+
+    return null;
+  }
+
+  @Override
+  public Void visitUpdate(Update node, Integer indent) {
+    builder
+        .append("UPDATE ")
+        .append(SqlFormatter.formatName(node.getTable().getName()))
+        .append(" SET");
+    int setCounter = node.getAssignments().size() - 1;
+    for (UpdateAssignment assignment : node.getAssignments()) {
+      builder
+          .append("\n")
+          .append(indentString(indent + 1))
+          .append(((Identifier) assignment.getName()).getValue())
+          .append(" = ")
+          .append(SqlFormatter.formatExpression(assignment.getValue()));
+      if (setCounter > 0) {
+        builder.append(",");
+      }
+      setCounter--;
+    }
+    node.getWhere()
+        .ifPresent(
+            where ->
+                builder
+                    .append("\n")
+                    .append(indentString(indent))
+                    .append("WHERE ")
+                    .append(SqlFormatter.formatExpression(where)));
+    return null;
+  }
+
+  @Override
+  public Void visitCreateFunction(CreateFunction node, Integer indent) {
+    builder
+        .append("CREATE FUNCTION ")
+        .append(node.getUdfName())
+        .append(" AS ")
+        .append(node.getClassName());
+    node.getUriString().ifPresent(uri -> builder.append(" USING URI 
").append(uri));
+    return null;
+  }
+
+  @Override
+  public Void visitDropFunction(DropFunction node, Integer indent) {
+    builder.append("DROP FUNCTION ");
+    builder.append(node.getUdfName());
+    return null;
+  }
+
+  @Override
+  public Void visitLoadTsFile(LoadTsFile node, Integer indent) {
+    builder.append("LOAD ");
+    builder.append("\"" + node.getFilePath() + "\"");
+    builder.append(" \n");
+
+    if (!node.getLoadAttributes().isEmpty()) {
+      builder
+          .append("WITH (")
+          .append("\n")
+          .append(
+              node.getLoadAttributes().entrySet().stream()
+                  .map(
+                      entry ->
+                          indentString(1)
+                              + "\""
+                              + entry.getKey()
+                              + "\" = \""
+                              + entry.getValue()
+                              + "\"")
+                  .collect(joining(", " + "\n")))
+          .append(")\n");
+    }
+    return null;
+  }
+
+  @Override
+  public Void visitCreatePipe(CreatePipe node, Integer context) {
+    builder.append("CREATE PIPE ");
+    if (node.hasIfNotExistsCondition()) {
+      builder.append("IF NOT EXISTS ");
+    }
+    builder.append(node.getPipeName());
+    builder.append(" \n");
+
+    if (!node.getSourceAttributes().isEmpty()) {
+      builder
+          .append("WITH SOURCE (")
+          .append("\n")
+          .append(
+              node.getSourceAttributes().entrySet().stream()
+                  .map(
+                      entry ->
+                          indentString(1)
+                              + "\""
+                              + entry.getKey()
+                              + "\" = \""
+                              + entry.getValue()
+                              + "\"")
+                  .collect(joining(", " + "\n")))
+          .append(")\n");
+    }
+
+    if (!node.getProcessorAttributes().isEmpty()) {
+      builder
+          .append("WITH PROCESSOR (")
+          .append("\n")
+          .append(
+              node.getProcessorAttributes().entrySet().stream()
+                  .map(
+                      entry ->
+                          indentString(1)
+                              + "\""
+                              + entry.getKey()
+                              + "\" = \""
+                              + entry.getValue()
+                              + "\"")
+                  .collect(joining(", " + "\n")))
+          .append(")\n");
+    }
+
+    if (!node.getSinkAttributes().isEmpty()) {
+      builder
+          .append("WITH SINK (")
+          .append("\n")
+          .append(
+              node.getSinkAttributes().entrySet().stream()
+                  .map(
+                      entry ->
+                          indentString(1)
+                              + "\""
+                              + entry.getKey()
+                              + "\" = \""
+                              + entry.getValue()
+                              + "\"")
+                  .collect(joining(", " + "\n")))
+          .append(")");
+    }
+
+    return null;
+  }
+
+  @Override
+  public Void visitAlterPipe(AlterPipe node, Integer context) {
+    builder.append("ALTER PIPE ");
+    if (node.hasIfExistsCondition()) {
+      builder.append("IF EXISTS ");
+    }
+    builder.append(node.getPipeName());
+    builder.append(" \n");
+
+    builder
+        .append(node.isReplaceAllExtractorAttributes() ? "REPLACE" : "MODIFY")
+        .append(" SOURCE (")
+        .append("\n")
+        .append(
+            node.getExtractorAttributes().entrySet().stream()
+                .map(
+                    entry ->
+                        indentString(1)
+                            + "\""
+                            + entry.getKey()
+                            + "\" = \""
+                            + entry.getValue()
+                            + "\"")
+                .collect(joining(", " + "\n")))
+        .append(")\n");
+
+    builder
+        .append(node.isReplaceAllProcessorAttributes() ? "REPLACE" : "MODIFY")
+        .append(" PROCESSOR (")
+        .append("\n")
+        .append(
+            node.getProcessorAttributes().entrySet().stream()
+                .map(
+                    entry ->
+                        indentString(1)
+                            + "\""
+                            + entry.getKey()
+                            + "\" = \""
+                            + entry.getValue()
+                            + "\"")
+                .collect(joining(", " + "\n")))
+        .append(")\n");
+
+    builder
+        .append(node.isReplaceAllConnectorAttributes() ? "REPLACE" : "MODIFY")
+        .append(" SINK (")
+        .append("\n")
+        .append(
+            node.getConnectorAttributes().entrySet().stream()
+                .map(
+                    entry ->
+                        indentString(1)
+                            + "\""
+                            + entry.getKey()
+                            + "\" = \""
+                            + entry.getValue()
+                            + "\"")
+                .collect(joining(", " + "\n")))
+        .append(")");
+
+    return null;
+  }
+
+  @Override
+  public Void visitDropPipe(DropPipe node, Integer context) {
+    builder.append("DROP PIPE ");
+    if (node.hasIfExistsCondition()) {
+      builder.append("IF EXISTS ");
+    }
+    builder.append(node.getPipeName());
+
+    return null;
+  }
+
+  @Override
+  public Void visitStartPipe(StartPipe node, Integer context) {
+    builder.append("START PIPE ").append(node.getPipeName());
+    return null;
+  }
+
+  @Override
+  public Void visitStopPipe(StopPipe node, Integer context) {
+    builder.append("STOP PIPE ").append(node.getPipeName());
+    return null;
+  }
+
+  @Override
+  public Void visitShowPipes(ShowPipes node, Integer context) {
+    builder.append("SHOW PIPES");
+    return null;
+  }
+
+  @Override
+  public Void visitCreatePipePlugin(CreatePipePlugin node, Integer context) {
+    builder.append("CREATE PIPEPLUGIN ");
+    if (node.hasIfNotExistsCondition()) {
+      builder.append("IF NOT EXISTS ");
+    }
+    builder.append(node.getPluginName());
+    builder.append("\n");
+
+    builder.append("AS \"");
+    builder.append(node.getClassName());
+    builder.append("\"\n");
+
+    builder.append("USING URI \"");
+    builder.append(node.getUriString());
+    builder.append("\"");
+
+    return null;
+  }
+
+  @Override
+  public Void visitDropPipePlugin(DropPipePlugin node, Integer context) {
+    builder.append("DROP PIPEPLUGIN ");
+    if (node.hasIfExistsCondition()) {
+      builder.append("IF EXISTS ");
+    }
+    builder.append(node.getPluginName());
+
+    return null;
+  }
+
+  @Override
+  public Void visitShowPipePlugins(ShowPipePlugins node, Integer context) {
+    builder.append("SHOW PIPEPLUGINS");
+    return null;
+  }
+
+  @Override
+  public Void visitCreateTopic(CreateTopic node, Integer context) {
+    builder.append("CREATE TOPIC ");
+    if (node.hasIfNotExistsCondition()) {
+      builder.append("IF NOT EXISTS ");
+    }
+    builder.append(node.getTopicName());
+    builder.append(" \n");
+
+    if (!node.getTopicAttributes().isEmpty()) {
+      builder
+          .append("WITH (")
+          .append("\n")
+          .append(
+              node.getTopicAttributes().entrySet().stream()
+                  .map(
+                      entry ->
+                          indentString(1)
+                              + "\""
+                              + entry.getKey()
+                              + "\" = \""
+                              + entry.getValue()
+                              + "\"")
+                  .collect(joining(", " + "\n")))
+          .append(")\n");
+    }
+
+    return null;
+  }
+
+  @Override
+  public Void visitDropTopic(DropTopic node, Integer context) {
+    builder.append("DROP TOPIC ");
+    if (node.hasIfExistsCondition()) {
+      builder.append("IF EXISTS ");
+    }
+    builder.append(node.getTopicName());
+
+    return null;
+  }
+
+  @Override
+  public Void visitShowTopics(ShowTopics node, Integer context) {
+    if (Objects.isNull(node.getTopicName())) {
+      builder.append("SHOW TOPICS");
+    } else {
+      builder.append("SHOW TOPIC ").append(node.getTopicName());
+    }
+
+    return null;
+  }
+
+  @Override
+  public Void visitShowSubscriptions(ShowSubscriptions node, Integer context) {
+    if (Objects.isNull(node.getTopicName())) {
+      builder.append("SHOW SUBSCRIPTIONS");
+    } else {
+      builder.append("SHOW SUBSCRIPTIONS ON ").append(node.getTopicName());
+    }
+
+    return null;
+  }
+
+  @Override
+  public Void visitDropSubscription(DropSubscription node, Integer context) {
+    builder.append("DROP SUBSCRIPTION ");
+    if (node.hasIfExistsCondition()) {
+      builder.append("IF EXISTS ");
+    }
+    builder.append(node.getSubscriptionId());
+
+    return null;
+  }
+
+  @Override
+  public Void visitRelationalAuthorPlan(RelationalAuthorStatement node, 
Integer context) {
+    switch (node.getAuthorType()) {
+      case GRANT_USER_ANY:
+        builder.append(
+            "GRANT "
+                + node.getPrivilegesString()
+                + " ON ANY"
+                + " TO USER "
+                + node.getUserName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case GRANT_USER_ALL:
+        builder.append(
+            "GRANT ALL TO USER "
+                + node.getUserName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case GRANT_USER_DB:
+        builder.append(
+            "GRANT "
+                + node.getPrivilegesString()
+                + " ON DATABASE "
+                + node.getDatabase()
+                + " TO USER "
+                + node.getUserName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case GRANT_USER_SYS:
+        builder.append(
+            "GRANT "
+                + node.getPrivilegesString()
+                + " TO USER "
+                + node.getUserName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case GRANT_USER_TB:
+        builder.append(
+            "GRANT "
+                + node.getPrivilegesString()
+                + " ON TABLE "
+                + node.getDatabase()
+                + "."
+                + node.getTableName()
+                + " TO USER "
+                + node.getUserName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case GRANT_ROLE_ANY:
+        builder.append(
+            "GRANT "
+                + node.getPrivilegesString()
+                + " ON ANY"
+                + " TO ROLE "
+                + node.getRoleName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case GRANT_ROLE_ALL:
+        builder.append(
+            "GRANT ALL TO ROLE "
+                + node.getRoleName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case GRANT_ROLE_DB:
+        builder.append(
+            "GRANT "
+                + node.getPrivilegesString()
+                + " ON DATABASE "
+                + node.getDatabase()
+                + " TO ROLE "
+                + node.getRoleName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case GRANT_ROLE_SYS:
+        builder.append(
+            "GRANT "
+                + node.getPrivilegesString()
+                + " TO ROLE "
+                + node.getRoleName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case GRANT_ROLE_TB:
+        builder.append(
+            "GRANT "
+                + node.getPrivilegesString()
+                + " ON TABLE "
+                + node.getDatabase()
+                + "."
+                + node.getTableName()
+                + " TO ROLE "
+                + node.getRoleName()
+                + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
+        break;
+      case REVOKE_USER_ANY:
+        builder.append(
+            "REVOKE "
+                + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
+                + node.getPrivilegesString()
+                + " ON ANY FROM USER "
+                + node.getUserName());
+        break;
+      case REVOKE_USER_ALL:
+        builder.append(
+            "REVOKE"
+                + (node.isGrantOption() ? "GRANT OPTION FOR ALL" : "ALL")
+                + " FROM USER "
+                + node.getUserName());
+        break;
+      case REVOKE_USER_DB:
+        builder.append(
+            "REVOKE "
+                + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
+                + node.getPrivilegesString()
+                + " ON DATABASE "
+                + node.getDatabase()
+                + " FROM USER "
+                + node.getUserName());
+        break;
+      case REVOKE_USER_SYS:
+        builder.append(
+            "REVOKE "
+                + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
+                + node.getPrivilegesString()
+                + "FROM USER "
+                + node.getUserName());
+        break;
+      case REVOKE_USER_TB:
+        builder.append(
+            "REVOKE "
+                + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
+                + node.getPrivilegesString()
+                + " ON TABLE "
+                + node.getDatabase()
+                + "."
+                + node.getTableName()
+                + " FROM USER "
+                + node.getUserName());
+        break;
+      case REVOKE_ROLE_ANY:
+        builder.append(
+            "REVOKE "
+                + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
+                + node.getPrivilegesString()
+                + " ON ANY FROM ROLE "
+                + node.getRoleName());
+        break;
+      case REVOKE_ROLE_ALL:
+        builder.append(
+            "REVOKE "
+                + (node.isGrantOption() ? "GRANT OPTION FOR ALL" : "ALL")
+                + " FROM ROLE "
+                + node.getRoleName());
+        break;
+      case REVOKE_ROLE_DB:
+        builder.append(
+            "REVOKE "
+                + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
+                + node.getPrivilegesString()
+                + " ON DATABASE "
+                + node.getDatabase()
+                + " FROM ROLE "
+                + node.getRoleName());
+        break;
+      case REVOKE_ROLE_SYS:
+        builder.append(
+            "REVOKE "
+                + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
+                + node.getPrivilegesString()
+                + " FROM ROLE "
+                + node.getRoleName());
+        break;
+      case REVOKE_ROLE_TB:
+        builder.append(
+            "REVOKE "
+                + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
+                + node.getPrivilegesString()
+                + " ON TABLE "
+                + node.getDatabase()
+                + "."
+                + node.getTableName()
+                + " FROM ROLE "
+                + node.getRoleName());
+        break;
+      case GRANT_USER_ROLE:
+        builder.append("GRANT ROLE " + node.getRoleName() + " TO " + 
node.getUserName());
+        break;
+      case REVOKE_USER_ROLE:
+        builder.append("REVOKE ROLE " + node.getRoleName() + " FROM " + 
node.getUserName());
+        break;
+      case CREATE_USER:
+        builder.append("CREATE USER " + node.getUserName());
+        break;
+      case CREATE_ROLE:
+        builder.append("CREATE ROLE " + node.getRoleName());
+        break;
+      case UPDATE_USER:
+        builder.append("ALTER USER " + node.getUserName() + " SET PASSWORD");
+        break;
+      case LIST_USER:
+        builder.append("LIST USER ");
+        break;
+      case LIST_ROLE:
+        builder.append("LIST ROLE ");
+        break;
+      case LIST_USER_PRIV:
+        builder.append("LIST PRIVILEGES OF USER " + node.getUserName());
+        break;
+      case LIST_ROLE_PRIV:
+        builder.append("LIST PRIVILEGES OF ROLE " + node.getRoleName());
+        break;
+      case DROP_ROLE:
+        builder.append("DROP ROLE " + node.getRoleName());
+        break;
+      case DROP_USER:
+        builder.append("DROP USER " + node.getUserName());
+        break;
+      default:
+        break;
+    }
+    return null;
+  }
+
+  @Override
+  public Void visitTableFunctionInvocation(TableFunctionInvocation node, 
Integer indent) {
+    append(indent, "TABLE(");
+    appendTableFunctionInvocation(node, indent + 1);
+    builder.append(")");
+    return null;
+  }
+
+  @Override
+  public Void visitTableArgument(TableFunctionTableArgument node, Integer 
indent) {
+    Relation relation = node.getTable();
+    Node unaliased =
+        relation instanceof AliasedRelation ? ((AliasedRelation) 
relation).getRelation() : relation;
+    if (unaliased instanceof TableSubquery) {
+      unaliased = ((TableSubquery) unaliased).getQuery();
+    }
+    builder.append("TABLE(");
+    process(unaliased, indent);
+    builder.append(")");
+    if (relation instanceof AliasedRelation) {
+      AliasedRelation aliasedRelation = (AliasedRelation) relation;
+      builder.append(" AS 
").append(SqlFormatter.formatName(aliasedRelation.getAlias()));
+      appendAliasColumns(builder, aliasedRelation.getColumnNames());
+    }
+    if (node.getPartitionBy().isPresent()) {
+      builder.append("\n");
+      append(indent, "PARTITION BY ")
+          .append(
+              node.getPartitionBy().get().stream()
+                  .map(SqlFormatter::formatExpression)
+                  .collect(joining(", ")));
+    }
+    node.getOrderBy()
+        .ifPresent(
+            orderBy -> {
+              builder.append("\n");
+              append(indent, formatOrderBy(orderBy));
+            });
+
+    return null;
+  }
+
+  @Override
+  protected boolean needsParenthesesForRelationSuffix(Relation relation) {
+    return super.needsParenthesesForRelationSuffix(relation)
+        || relation instanceof PatternRecognitionRelation;
+  }
+
+  @Override
+  protected boolean isSimpleTableRelation(Relation relation) {
+    return relation instanceof Table;
+  }
+
+  @Override
+  protected String formatSimpleTableRelationName(Relation relation) {
+    return SqlFormatter.formatName(((Table) relation).getName());
+  }
+
+  private String formatPropertiesMultiLine(List<Property> properties) {
+    if (properties.isEmpty()) {
+      return "";
+    }
+
+    String propertyList =
+        properties.stream()
+            .map(
+                element ->
+                    SqlFormatter.INDENT
+                        + SqlFormatter.formatName(element.getName())
+                        + " = "
+                        + (element.isSetToDefault()
+                            ? "DEFAULT"
+                            : 
SqlFormatter.formatExpression(element.getNonDefaultValue())))
+            .collect(joining(",\n"));
+
+    return "\nWITH (\n" + propertyList + "\n)";
+  }
+
+  private String formatColumnDefinition(ColumnDefinition column) {
+    StringBuilder stringBuilder =
+        new StringBuilder()
+            .append(SqlFormatter.formatName(column.getName()))
+            .append(" ")
+            .append(column.getType())
+            .append(" ")
+            .append(column.getColumnCategory());
+
+    column.getCharsetName().ifPresent(charset -> stringBuilder.append(" 
CHARSET ").append(charset));
+
+    if (Objects.nonNull(column.getComment())) {
+      stringBuilder.append(" COMMENT 
'").append(column.getComment()).append("'");
+    }
+    return stringBuilder.toString();
+  }
+
+  private String joinProperties(List<Property> properties) {
+    return properties.stream()
+        .map(
+            element ->
+                SqlFormatter.formatName(element.getName())
+                    + " = "
+                    + (element.isSetToDefault()
+                        ? "DEFAULT"
+                        : 
SqlFormatter.formatExpression(element.getNonDefaultValue())))
+        .collect(joining(", "));
+  }
+
+  private void appendTableFunctionInvocation(TableFunctionInvocation node, 
Integer indent) {
+    builder.append(SqlFormatter.formatName(node.getName())).append("(\n");
+    appendTableFunctionArguments(node.getArguments(), indent + 1);
+    builder.append(")");
+  }
+
+  private void appendTableFunctionArguments(List<TableFunctionArgument> 
arguments, int indent) {
+    for (int i = 0; i < arguments.size(); i++) {
+      TableFunctionArgument argument = arguments.get(i);
+      if (argument.getName().isPresent()) {
+        append(indent, SqlFormatter.formatName(argument.getName().get()));
+        builder.append(" => ");
+      } else {
+        append(indent, "");
+      }
+      Node value = argument.getValue();
+      if (value instanceof Expression) {
+        builder.append(SqlFormatter.formatExpression((Expression) value));
+      } else {
+        process(value, indent + 1);
+      }
+      if (i < arguments.size() - 1) {
+        builder.append(",\n");
+      }
+    }
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java
index dc59e32c76f..69bfe239750 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java
@@ -19,126 +19,30 @@
 
 package org.apache.iotdb.db.queryengine.plan.relational.sql.util;
 
-import 
org.apache.iotdb.db.node_commons.plan.relational.sql.ast.AliasedRelation;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.AllColumns;
 import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Expression;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Fill;
 import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Identifier;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Join;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.JoinCriteria;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Limit;
 import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Node;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Offset;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.OrderBy;
 import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.QualifiedName;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Query;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Relation;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Row;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.SelectItem;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.SingleColumn;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.TableSubquery;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.Values;
-import org.apache.iotdb.db.node_commons.plan.relational.sql.ast.WithQuery;
 import 
org.apache.iotdb.db.node_commons.plan.relational.sql.util.ExpressionFormatter;
-import org.apache.iotdb.db.node_commons.plan.statement.component.FillPolicy;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AddColumn;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterDB;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterPipe;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ColumnDefinition;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CopyTo;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipe;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipePlugin;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTable;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTopic;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateView;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Delete;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipe;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipePlugin;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropSubscription;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropTable;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropTopic;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Except;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Explain;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExplainAnalyze;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Insert;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Intersect;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.JoinOn;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.JoinUsing;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.LoadTsFile;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.NaturalJoin;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.PatternRecognitionRelation;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Property;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.QuerySpecification;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameColumn;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameTable;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RowPattern;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Select;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetColumnComment;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetProperties;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetTableComment;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowClusterId;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentDatabase;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentSqlDialect;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentTimestamp;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentUser;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowFunctions;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowPipePlugins;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowPipes;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowSubscriptions;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowTables;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowTopics;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowVariables;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowVersion;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartPipe;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopPipe;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Table;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TableFunctionArgument;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TableFunctionInvocation;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TableFunctionTableArgument;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Union;
-import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Update;
-import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.UpdateAssignment;
 
-import com.google.common.base.Joiner;
-import com.google.common.base.Strings;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.Iterables.getOnlyElement;
 import static java.util.Objects.requireNonNull;
 import static java.util.stream.Collectors.joining;
-import static 
org.apache.iotdb.db.node_commons.plan.relational.sql.util.ExpressionFormatter.formatGroupBy;
-import static 
org.apache.iotdb.db.node_commons.plan.relational.sql.util.ExpressionFormatter.formatOrderBy;
-import static 
org.apache.iotdb.db.queryengine.plan.relational.sql.util.RowPatternFormatter.formatPattern;
 
 public final class SqlFormatter {
 
-  private static final String INDENT = "   ";
+  protected static final String INDENT = "   ";
 
   private SqlFormatter() {}
 
   public static String formatSql(Node root) {
     StringBuilder builder = new StringBuilder();
-    new Formatter(builder).process(root, 0);
+    new DataNodeSqlFormatter(builder).process(root, 0);
     return builder.toString();
   }
 
-  private static String formatName(Identifier identifier) {
+  static String formatName(Identifier identifier) {
     return ExpressionFormatter.formatExpression(identifier);
   }
 
@@ -146,1622 +50,27 @@ public final class SqlFormatter {
     return 
name.getOriginalParts().stream().map(SqlFormatter::formatName).collect(joining("."));
   }
 
-  private static String formatExpression(Expression expression) {
+  static String formatExpression(Expression expression) {
     return ExpressionFormatter.formatExpression(expression);
   }
 
-  private static class Formatter implements AstVisitor<Void, Integer> {
-    private static class SqlBuilder {
-      private final StringBuilder builder;
-
-      public SqlBuilder(StringBuilder builder) {
-        this.builder = requireNonNull(builder, "builder is null");
-      }
-
-      @CanIgnoreReturnValue
-      public SqlBuilder append(CharSequence value) {
-        builder.append(value);
-        return this;
-      }
-
-      @CanIgnoreReturnValue
-      public SqlBuilder append(char c) {
-        builder.append(c);
-        return this;
-      }
-    }
-
-    private final SqlBuilder builder;
-
-    public Formatter(StringBuilder builder) {
-      this.builder = new SqlBuilder(builder);
-    }
-
-    @Override
-    public Void visitNode(Node node, Integer indent) {
-      throw new UnsupportedOperationException("not yet implemented: " + node);
-    }
-
-    @Override
-    public Void visitExpression(Expression node, Integer indent) {
-      checkArgument(indent == 0, "visitExpression should only be called at 
root");
-      builder.append(formatExpression(node));
-      return null;
-    }
-
-    @Override
-    public Void visitRowPattern(RowPattern node, Integer indent) {
-      checkArgument(indent == 0, "visitRowPattern should only be called at 
root");
-      builder.append(formatPattern(node));
-      return null;
-    }
-
-    @Override
-    public Void visitQuery(Query node, Integer indent) {
-
-      node.getWith()
-          .ifPresent(
-              with -> {
-                append(indent, "WITH");
-                if (with.isRecursive()) {
-                  builder.append(" RECURSIVE");
-                }
-                builder.append("\n  ");
-                Iterator<WithQuery> queries = with.getQueries().iterator();
-                while (queries.hasNext()) {
-                  WithQuery query = queries.next();
-                  append(indent, formatName(query.getName()));
-                  query
-                      .getColumnNames()
-                      .ifPresent(columnNames -> appendAliasColumns(builder, 
columnNames));
-                  builder.append(" AS ");
-                  process(new TableSubquery(query.getQuery()), indent);
-                  builder.append('\n');
-                  if (queries.hasNext()) {
-                    builder.append(", ");
-                  }
-                }
-              });
-
-      processRelation(node.getQueryBody(), indent);
-      node.getOrderBy().ifPresent(orderBy -> process(orderBy, indent));
-      node.getOffset().ifPresent(offset -> process(offset, indent));
-      node.getLimit().ifPresent(limit -> process(limit, indent));
-      return null;
-    }
-
-    @Override
-    public Void visitQuerySpecification(QuerySpecification node, Integer 
indent) {
-      process(node.getSelect(), indent);
-
-      node.getFrom()
-          .ifPresent(
-              from -> {
-                append(indent, "FROM");
-                builder.append('\n');
-                append(indent, "  ");
-                process(from, indent);
-              });
-
-      builder.append('\n');
-
-      node.getWhere()
-          .ifPresent(where -> append(indent, "WHERE " + 
formatExpression(where)).append('\n'));
-
-      node.getGroupBy()
-          .ifPresent(
-              groupBy ->
-                  append(
-                          indent,
-                          "GROUP BY "
-                              + (groupBy.isDistinct() ? " DISTINCT " : "")
-                              + formatGroupBy(groupBy.getGroupingElements()))
-                      .append('\n'));
-
-      node.getHaving()
-          .ifPresent(having -> append(indent, "HAVING " + 
formatExpression(having)).append('\n'));
-
-      node.getOrderBy().ifPresent(orderBy -> process(orderBy, indent));
-      node.getOffset().ifPresent(offset -> process(offset, indent));
-      node.getLimit().ifPresent(limit -> process(limit, indent));
-      return null;
-    }
-
-    @Override
-    public Void visitFill(Fill node, Integer indent) {
-      append(indent, "FILL METHOD ").append(node.getFillMethod().name());
-
-      if (node.getFillMethod() == FillPolicy.CONSTANT) {
-        builder.append(formatExpression(node.getFillValue().get()));
-      } else if (node.getFillMethod() == FillPolicy.LINEAR) {
-        node.getTimeColumnIndex()
-            .ifPresent(index -> builder.append(" TIME_COLUMN 
").append(String.valueOf(index)));
-        node.getFillGroupingElements()
-            .ifPresent(
-                elements ->
-                    builder
-                        .append(" FILL_GROUP ")
-                        .append(
-                            elements.stream()
-                                .map(SqlFormatter::formatExpression)
-                                .collect(joining(", "))));
-      } else if (node.getFillMethod() == FillPolicy.PREVIOUS) {
-        node.getTimeBound()
-            .ifPresent(timeBound -> builder.append(" TIME_BOUND 
").append(timeBound.toString()));
-        node.getTimeColumnIndex()
-            .ifPresent(index -> builder.append(" TIME_COLUMN 
").append(String.valueOf(index)));
-        node.getFillGroupingElements()
-            .ifPresent(
-                elements ->
-                    builder
-                        .append(" FILL_GROUP ")
-                        .append(
-                            elements.stream()
-                                .map(SqlFormatter::formatExpression)
-                                .collect(joining(", "))));
-      } else {
-        throw new IllegalArgumentException("Unknown fill method: " + 
node.getFillMethod());
-      }
-      return null;
-    }
-
-    @Override
-    public Void visitOrderBy(OrderBy node, Integer indent) {
-      append(indent, formatOrderBy(node)).append('\n');
-      return null;
-    }
-
-    @Override
-    public Void visitOffset(Offset node, Integer indent) {
-      append(indent, "OFFSET 
").append(formatExpression(node.getRowCount())).append(" ROWS\n");
-      return null;
-    }
-
-    @Override
-    public Void visitLimit(Limit node, Integer indent) {
-      append(indent, "LIMIT 
").append(formatExpression(node.getRowCount())).append('\n');
-      return null;
-    }
-
-    @Override
-    public Void visitSelect(Select node, Integer indent) {
-      append(indent, "SELECT");
-      if (node.isDistinct()) {
-        builder.append(" DISTINCT");
-      }
-
-      if (node.getSelectItems().size() > 1) {
-        boolean first = true;
-        for (SelectItem item : node.getSelectItems()) {
-          builder.append("\n").append(indentString(indent)).append(first ? "  
" : ", ");
-
-          process(item, indent);
-          first = false;
-        }
-      } else {
-        builder.append(' ');
-        process(getOnlyElement(node.getSelectItems()), indent);
-      }
-
-      builder.append('\n');
-
-      return null;
-    }
-
-    @Override
-    public Void visitSingleColumn(SingleColumn node, Integer indent) {
-      builder.append(formatExpression(node.getExpression()));
-      node.getAlias().ifPresent(alias -> builder.append(' 
').append(formatName(alias)));
-
-      return null;
-    }
-
-    @Override
-    public Void visitAllColumns(AllColumns node, Integer indent) {
-      node.getTarget().ifPresent(value -> 
builder.append(formatExpression(value)).append("."));
-      builder.append("*");
-
-      if (!node.getAliases().isEmpty()) {
-        builder
-            .append(" AS (")
-            .append(
-                Joiner.on(", ")
-                    .join(
-                        node.getAliases().stream()
-                            .map(SqlFormatter::formatName)
-                            .collect(toImmutableList())))
-            .append(")");
-      }
-
-      return null;
-    }
-
-    @Override
-    public Void visitTable(Table node, Integer indent) {
-      builder.append(formatName(node.getName()));
-      return null;
-    }
-
-    @Override
-    public Void visitJoin(Join node, Integer indent) {
-      JoinCriteria criteria = node.getCriteria().orElse(null);
-      String type = node.getType().toString();
-      if (criteria instanceof NaturalJoin) {
-        type = "NATURAL " + type;
-      }
-
-      if (node.getType() != Join.Type.IMPLICIT) {
-        builder.append('(');
-      }
-      process(node.getLeft(), indent);
-
-      builder.append('\n');
-      if (node.getType() == Join.Type.IMPLICIT) {
-        append(indent, ", ");
-      } else {
-        append(indent, type).append(" JOIN ");
-      }
-
-      process(node.getRight(), indent);
-
-      if (node.getType() != Join.Type.CROSS && node.getType() != 
Join.Type.IMPLICIT) {
-        if (criteria instanceof JoinUsing) {
-          JoinUsing using = (JoinUsing) criteria;
-          builder.append(" USING (").append(Joiner.on(", 
").join(using.getColumns())).append(")");
-        } else if (criteria instanceof JoinOn) {
-          JoinOn on = (JoinOn) criteria;
-          builder.append(" ON ").append(formatExpression(on.getExpression()));
-        } else if (!(criteria instanceof NaturalJoin)) {
-          throw new UnsupportedOperationException("unknown join criteria: " + 
criteria);
-        }
-      }
-
-      if (node.getType() != Join.Type.IMPLICIT) {
-        builder.append(")");
-      }
-
-      return null;
-    }
-
-    @Override
-    public Void visitAliasedRelation(AliasedRelation node, Integer indent) {
-      processRelationSuffix(node.getRelation(), indent);
-
-      builder.append(' ').append(formatName(node.getAlias()));
-      appendAliasColumns(builder, node.getColumnNames());
-
-      return null;
-    }
-
-    @Override
-    public Void visitPatternRecognitionRelation(PatternRecognitionRelation 
node, Integer indent) {
-      processRelationSuffix(node.getInput(), indent);
-
-      builder.append(" MATCH_RECOGNIZE (\n");
-      if (!node.getPartitionBy().isEmpty()) {
-        append(indent + 1, "PARTITION BY ")
-            .append(
-                node.getPartitionBy().stream()
-                    .map(ExpressionFormatter::formatExpression)
-                    .collect(joining(", ")))
-            .append("\n");
-      }
-      if (node.getOrderBy().isPresent()) {
-        process(node.getOrderBy().get(), indent + 1);
-      }
-      if (!node.getMeasures().isEmpty()) {
-        append(indent + 1, "MEASURES");
-        formatDefinitionList(
-            node.getMeasures().stream()
-                .map(
-                    measure ->
-                        formatExpression(measure.getExpression())
-                            + " AS "
-                            + formatExpression(measure.getName()))
-                .collect(toImmutableList()),
-            indent + 2);
-      }
-      if (node.getRowsPerMatch().isPresent()) {
-        String rowsPerMatch;
-        switch (node.getRowsPerMatch().get()) {
-          case ONE:
-            rowsPerMatch = "ONE ROW PER MATCH";
-            break;
-          case ALL_SHOW_EMPTY:
-            rowsPerMatch = "ALL ROWS PER MATCH SHOW EMPTY MATCHES";
-            break;
-          case ALL_OMIT_EMPTY:
-            rowsPerMatch = "ALL ROWS PER MATCH OMIT EMPTY MATCHES";
-            break;
-          case ALL_WITH_UNMATCHED:
-            rowsPerMatch = "ALL ROWS PER MATCH WITH UNMATCHED ROWS";
-            break;
-          default:
-            // RowsPerMatch of type WINDOW cannot occur in MATCH_RECOGNIZE 
clause
-            throw new IllegalStateException(
-                "unexpected rowsPerMatch: " + node.getRowsPerMatch().get());
-        }
-        append(indent + 1, rowsPerMatch).append("\n");
-      }
-      if (node.getAfterMatchSkipTo().isPresent()) {
-        String skipTo;
-        switch (node.getAfterMatchSkipTo().get().getPosition()) {
-          case PAST_LAST:
-            skipTo = "AFTER MATCH SKIP PAST LAST ROW";
-            break;
-          case NEXT:
-            skipTo = "AFTER MATCH SKIP TO NEXT ROW";
-            break;
-          case LAST:
-            checkState(
-                node.getAfterMatchSkipTo().get().getIdentifier().isPresent(),
-                "missing identifier in AFTER MATCH SKIP TO LAST");
-            skipTo =
-                "AFTER MATCH SKIP TO LAST "
-                    + 
formatExpression(node.getAfterMatchSkipTo().get().getIdentifier().get());
-            break;
-          case FIRST:
-            checkState(
-                node.getAfterMatchSkipTo().get().getIdentifier().isPresent(),
-                "missing identifier in AFTER MATCH SKIP TO FIRST");
-            skipTo =
-                "AFTER MATCH SKIP TO FIRST "
-                    + 
formatExpression(node.getAfterMatchSkipTo().get().getIdentifier().get());
-            break;
-          default:
-            throw new IllegalStateException(
-                "unexpected skipTo: " + node.getAfterMatchSkipTo().get());
-        }
-        append(indent + 1, skipTo).append("\n");
-      }
-      append(indent + 1, "PATTERN 
(").append(formatPattern(node.getPattern())).append(")\n");
-      if (!node.getSubsets().isEmpty()) {
-        append(indent + 1, "SUBSET");
-        formatDefinitionList(
-            node.getSubsets().stream()
-                .map(
-                    subset ->
-                        formatExpression(subset.getName())
-                            + " = "
-                            + subset.getIdentifiers().stream()
-                                .map(ExpressionFormatter::formatExpression)
-                                .collect(joining(", ", "(", ")")))
-                .collect(toImmutableList()),
-            indent + 2);
-      }
-      append(indent + 1, "DEFINE");
-      formatDefinitionList(
-          node.getVariableDefinitions().stream()
-              .map(
-                  variable ->
-                      formatExpression(variable.getName())
-                          + " AS "
-                          + formatExpression(variable.getExpression()))
-              .collect(toImmutableList()),
-          indent + 2);
-
-      builder.append(")");
-
-      return null;
-    }
-
-    private void processRelationSuffix(Relation relation, Integer indent) {
-      if ((relation instanceof AliasedRelation)
-          || (relation instanceof PatternRecognitionRelation)) {
-        builder.append("( ");
-        process(relation, indent + 1);
-        append(indent, ")");
-      } else {
-        process(relation, indent);
-      }
-    }
-
-    @Override
-    public Void visitValues(Values node, Integer indent) {
-      builder.append(" VALUES ");
-
-      boolean first = true;
-      for (Expression row : node.getRows()) {
-        builder.append("\n").append(indentString(indent)).append(first ? "  " 
: ", ");
-
-        builder.append(formatExpression(row));
-        first = false;
-      }
-      builder.append('\n');
-
-      return null;
-    }
-
-    @Override
-    public Void visitTableSubquery(TableSubquery node, Integer indent) {
-      builder.append('(').append('\n');
-
-      process(node.getQuery(), indent + 1);
+  protected static class SqlBuilder {
+    private final StringBuilder builder;
 
-      append(indent, ") ");
-
-      return null;
+    public SqlBuilder(StringBuilder builder) {
+      this.builder = requireNonNull(builder, "builder is null");
     }
 
-    @Override
-    public Void visitUnion(Union node, Integer indent) {
-      Iterator<Relation> relations = node.getRelations().iterator();
-
-      while (relations.hasNext()) {
-        processRelation(relations.next(), indent);
-
-        if (relations.hasNext()) {
-          builder.append("UNION ");
-          if (!node.isDistinct()) {
-            builder.append("ALL ");
-          }
-        }
-      }
-
-      return null;
+    @CanIgnoreReturnValue
+    public SqlBuilder append(CharSequence value) {
+      builder.append(value);
+      return this;
     }
 
-    @Override
-    public Void visitExcept(Except node, Integer indent) {
-      processRelation(node.getLeft(), indent);
-
-      builder.append("EXCEPT ");
-      if (!node.isDistinct()) {
-        builder.append("ALL ");
-      }
-
-      processRelation(node.getRight(), indent);
-
-      return null;
-    }
-
-    @Override
-    public Void visitIntersect(Intersect node, Integer indent) {
-      Iterator<Relation> relations = node.getRelations().iterator();
-
-      while (relations.hasNext()) {
-        processRelation(relations.next(), indent);
-
-        if (relations.hasNext()) {
-          builder.append("INTERSECT ");
-          if (!node.isDistinct()) {
-            builder.append("ALL ");
-          }
-        }
-      }
-
-      return null;
-    }
-
-    @Override
-    public Void visitExplain(Explain node, Integer indent) {
-      builder.append("EXPLAIN ");
-
-      builder.append("\n");
-
-      process(node.getStatement(), indent);
-
-      return null;
-    }
-
-    @Override
-    public Void visitCopyTo(CopyTo node, Integer context) {
-      builder.append("COPY\n");
-      builder.append("(\n");
-      process(node.getQueryStatement(), context);
-      builder.append("\n) ");
-      builder.append("TO ");
-      builder.append('\'');
-      builder.append(node.getTargetFileName());
-      builder.append('\'');
-      builder.append("\n");
-      builder.append(node.getOptions().toString());
-      return null;
-    }
-
-    @Override
-    public Void visitExplainAnalyze(ExplainAnalyze node, Integer indent) {
-      builder.append("EXPLAIN ANALYZE");
-      if (node.isVerbose()) {
-        builder.append(" VERBOSE");
-      }
-      builder.append("\n");
-
-      process(node.getStatement(), indent);
-
-      return null;
-    }
-
-    @Override
-    public Void visitShowDB(ShowDB node, Integer indent) {
-      builder.append("SHOW DATABASE");
-
-      return null;
-    }
-
-    @Override
-    public Void visitShowTables(final ShowTables node, final Integer indent) {
-      builder.append("SHOW TABLES");
-
-      if (node.isDetails()) {
-        builder.append(" DETAILS");
-      }
-
-      node.getDbName().ifPresent(db -> builder.append(" FROM 
").append(formatName(db)));
-
-      return null;
-    }
-
-    @Override
-    public Void visitShowFunctions(ShowFunctions node, Integer indent) {
-      builder.append("SHOW FUNCTIONS");
-
-      return null;
-    }
-
-    @Override
-    public Void visitShowCurrentSqlDialect(ShowCurrentSqlDialect node, Integer 
context) {
-      builder.append(node.toString());
-      return null;
-    }
-
-    @Override
-    public Void visitShowCurrentDatabase(ShowCurrentDatabase node, Integer 
context) {
-      builder.append(node.toString());
-      return null;
-    }
-
-    @Override
-    public Void visitShowCurrentUser(ShowCurrentUser node, Integer context) {
-      builder.append(node.toString());
-      return null;
-    }
-
-    @Override
-    public Void visitShowVersion(ShowVersion node, Integer context) {
-      builder.append(node.toString());
-      return null;
-    }
-
-    @Override
-    public Void visitShowVariables(ShowVariables node, Integer context) {
-      builder.append(node.toString());
-      return null;
-    }
-
-    @Override
-    public Void visitShowClusterId(ShowClusterId node, Integer context) {
-      builder.append(node.toString());
-      return null;
-    }
-
-    @Override
-    public Void visitShowCurrentTimestamp(ShowCurrentTimestamp node, Integer 
context) {
-      builder.append(node.toString());
-      return null;
-    }
-
-    @Override
-    public Void visitDelete(final Delete node, final Integer indent) {
-      builder.append("DELETE FROM 
").append(formatName(node.getTable().getName()));
-
-      node.getWhere().ifPresent(where -> builder.append(" WHERE 
").append(formatExpression(where)));
-
-      return null;
-    }
-
-    @Override
-    public Void visitCreateDB(final CreateDB node, final Integer indent) {
-      builder.append("CREATE DATABASE ");
-      if (node.exists()) {
-        builder.append("IF NOT EXISTS ");
-      }
-      builder.append(node.getDbName()).append(" ");
-
-      builder.append(formatPropertiesMultiLine(node.getProperties()));
-
-      return null;
-    }
-
-    @Override
-    public Void visitAlterDB(final AlterDB node, final Integer indent) {
-      builder.append("ALTER DATABASE ");
-      if (node.exists()) {
-        builder.append("IF EXISTS ");
-      }
-      builder.append(node.getDbName()).append(" ");
-
-      builder.append(formatPropertiesMultiLine(node.getProperties()));
-
-      return null;
-    }
-
-    @Override
-    public Void visitDropDB(final DropDB node, final Integer indent) {
-      builder.append("DROP DATABASE ");
-      if (node.isExists()) {
-        builder.append("IF EXISTS ");
-      }
-      builder.append(formatName(node.getDbName()));
-      return null;
-    }
-
-    @Override
-    public Void visitCreateTable(final CreateTable node, final Integer indent) 
{
-      builder.append("CREATE TABLE ");
-      if (node.isIfNotExists()) {
-        builder.append("IF NOT EXISTS ");
-      }
-      final String tableName = formatName(node.getName());
-      builder.append(tableName).append(" (\n");
-
-      final String elementIndent = indentString(indent + 1);
-      final String columnList =
-          node.getElements().stream()
-              .map(
-                  element -> {
-                    if (element != null) {
-                      return elementIndent + formatColumnDefinition(element);
-                    }
-
-                    throw new UnsupportedOperationException("unknown table 
element: " + element);
-                  })
-              .collect(joining(",\n"));
-      builder.append(columnList);
-      builder.append("\n").append(")");
-
-      node.getCharsetName().ifPresent(charset -> builder.append(" CHARSET 
").append(charset));
-
-      if (Objects.nonNull(node.getComment())) {
-        builder.append(" COMMENT '").append(node.getComment()).append("'");
-      }
-
-      builder.append(formatPropertiesMultiLine(node.getProperties()));
-
-      return null;
-    }
-
-    @Override
-    public Void visitCreateView(final CreateView node, final Integer indent) {
-      builder.append("CREATE ");
-      if (node.isReplace()) {
-        builder.append("OR REPLACE ");
-      }
-      builder.append("VIEW ");
-      final String tableName = formatName(node.getName());
-      builder.append(tableName).append(" (\n");
-
-      final String elementIndent = indentString(indent + 1);
-      final String columnList =
-          node.getElements().stream()
-              .map(
-                  element -> {
-                    if (element != null) {
-                      return elementIndent + formatColumnDefinition(element);
-                    }
-
-                    throw new UnsupportedOperationException("unknown table 
element: " + element);
-                  })
-              .collect(joining(",\n"));
-      builder.append(columnList);
-      builder.append("\n").append(")");
-
-      if (Objects.nonNull(node.getComment())) {
-        builder.append(" COMMENT '").append(node.getComment()).append("'");
-      }
-
-      if (node.isRestrict()) {
-        builder.append(" RESTRICT");
-      }
-
-      builder.append(formatPropertiesMultiLine(node.getProperties()));
-
-      builder.append(" AS ").append(node.getPrefixPath().toString());
-
-      return null;
-    }
-
-    private String formatPropertiesMultiLine(List<Property> properties) {
-      if (properties.isEmpty()) {
-        return "";
-      }
-
-      String propertyList =
-          properties.stream()
-              .map(
-                  element ->
-                      INDENT
-                          + formatName(element.getName())
-                          + " = "
-                          + (element.isSetToDefault()
-                              ? "DEFAULT"
-                              : 
formatExpression(element.getNonDefaultValue())))
-              .collect(joining(",\n"));
-
-      return "\nWITH (\n" + propertyList + "\n)";
-    }
-
-    private String formatPropertiesSingleLine(List<Property> properties) {
-      if (properties.isEmpty()) {
-        return "";
-      }
-
-      return " WITH ( " + joinProperties(properties) + " )";
-    }
-
-    private String formatColumnDefinition(ColumnDefinition column) {
-      StringBuilder stringBuilder =
-          new StringBuilder()
-              .append(formatName(column.getName()))
-              .append(" ")
-              .append(column.getType())
-              .append(" ")
-              .append(column.getColumnCategory());
-
-      column
-          .getCharsetName()
-          .ifPresent(charset -> stringBuilder.append(" CHARSET 
").append(charset));
-
-      if (Objects.nonNull(column.getComment())) {
-        stringBuilder.append(" COMMENT 
'").append(column.getComment()).append("'");
-      }
-      return stringBuilder.toString();
-    }
-
-    @Override
-    public Void visitDropTable(final DropTable node, final Integer indent) {
-      builder.append("DROP");
-      builder.append(node.isView() ? " VIEW " : " TABLE ");
-      if (node.isExists()) {
-        builder.append("IF EXISTS ");
-      }
-      builder.append(formatName(node.getTableName()));
-
-      return null;
-    }
-
-    @Override
-    public Void visitRenameTable(final RenameTable node, final Integer indent) 
{
-      builder.append("ALTER");
-      builder.append(node.isView() ? " VIEW " : " TABLE ");
-      if (node.tableIfExists()) {
-        builder.append("IF EXISTS ");
-      }
-
-      builder
-          .append(formatName(node.getSource()))
-          .append(" RENAME TO ")
-          .append(formatName(node.getTarget()));
-
-      return null;
-    }
-
-    @Override
-    public Void visitSetProperties(final SetProperties node, final Integer 
context) {
-      final SetProperties.Type type = node.getType();
-      builder.append("ALTER ");
-      switch (type) {
-        case TABLE:
-          builder.append("TABLE ");
-        case MATERIALIZED_VIEW:
-          builder.append("MATERIALIZED VIEW ");
-        case TREE_VIEW:
-          builder.append("VIEW ");
-      }
-      if (node.ifExists()) {
-        builder.append("IF EXISTS ");
-      }
-
-      builder
-          .append(formatName(node.getName()))
-          .append(" SET PROPERTIES ")
-          .append(joinProperties(node.getProperties()));
-
-      return null;
-    }
-
-    private String joinProperties(List<Property> properties) {
-      return properties.stream()
-          .map(
-              element ->
-                  formatName(element.getName())
-                      + " = "
-                      + (element.isSetToDefault()
-                          ? "DEFAULT"
-                          : formatExpression(element.getNonDefaultValue())))
-          .collect(joining(", "));
-    }
-
-    @Override
-    public Void visitRenameColumn(RenameColumn node, Integer indent) {
-      builder.append("ALTER");
-      builder.append(node.isView() ? " VIEW " : " TABLE ");
-      if (node.tableIfExists()) {
-        builder.append("IF EXISTS ");
-      }
-
-      builder.append(formatName(node.getTable())).append("RENAME COLUMN ");
-      if (node.columnIfExists()) {
-        builder.append("IF EXISTS ");
-      }
-
-      builder
-          .append(formatName(node.getSource()))
-          .append(" TO ")
-          .append(formatName(node.getTarget()));
-
-      return null;
-    }
-
-    @Override
-    public Void visitDropColumn(final DropColumn node, final Integer indent) {
-      builder.append("ALTER");
-      builder.append(node.isView() ? " VIEW " : " TABLE ");
-      if (node.tableIfExists()) {
-        builder.append("IF EXISTS ");
-      }
-
-      builder.append(formatName(node.getTable())).append("DROP COLUMN ");
-      if (node.columnIfExists()) {
-        builder.append("IF EXISTS ");
-      }
-
-      builder.append(formatName(node.getField()));
-
-      return null;
-    }
-
-    @Override
-    public Void visitAddColumn(final AddColumn node, final Integer indent) {
-      builder.append("ALTER");
-      builder.append(node.isView() ? " VIEW " : " TABLE ");
-      if (node.tableIfExists()) {
-        builder.append("IF EXISTS ");
-      }
-
-      builder.append(formatName(node.getTableName())).append("ADD COLUMN ");
-      if (node.columnIfNotExists()) {
-        builder.append("IF NOT EXISTS ");
-      }
-
-      builder.append(formatColumnDefinition(node.getColumn()));
-
-      return null;
-    }
-
-    @Override
-    public Void visitSetTableComment(final SetTableComment node, final Integer 
indent) {
-      builder
-          .append("COMMENT ON")
-          .append(node.isView() ? " VIEW " : " TABLE ")
-          .append(formatName(node.getTableName()))
-          .append(" IS ")
-          .append(node.getComment());
-      return null;
-    }
-
-    @Override
-    public Void visitSetColumnComment(final SetColumnComment node, final 
Integer indent) {
-      builder
-          .append("COMMENT ON COLUMN ")
-          .append(formatName(node.getTable()))
-          .append(".")
-          .append(formatName(node.getField()))
-          .append(" IS ")
-          .append(node.getComment());
-      return null;
-    }
-
-    @Override
-    public Void visitInsert(Insert node, Integer indent) {
-      builder.append("INSERT INTO ").append(formatName(node.getTarget()));
-
-      node.getColumns()
-          .ifPresent(
-              columns -> builder.append(" (").append(Joiner.on(", 
").join(columns)).append(")"));
-
-      builder.append("\n");
-
-      process(node.getQuery(), indent);
-
-      return null;
-    }
-
-    @Override
-    public Void visitUpdate(Update node, Integer indent) {
-      builder.append("UPDATE 
").append(formatName(node.getTable().getName())).append(" SET");
-      int setCounter = node.getAssignments().size() - 1;
-      for (UpdateAssignment assignment : node.getAssignments()) {
-        builder
-            .append("\n")
-            .append(indentString(indent + 1))
-            .append(((Identifier) assignment.getName()).getValue())
-            .append(" = ")
-            .append(formatExpression(assignment.getValue()));
-        if (setCounter > 0) {
-          builder.append(",");
-        }
-        setCounter--;
-      }
-      node.getWhere()
-          .ifPresent(
-              where ->
-                  builder
-                      .append("\n")
-                      .append(indentString(indent))
-                      .append("WHERE ")
-                      .append(formatExpression(where)));
-      return null;
-    }
-
-    @Override
-    public Void visitRow(Row node, Integer indent) {
-      builder.append("ROW(");
-      boolean firstItem = true;
-      for (Expression item : node.getItems()) {
-        if (!firstItem) {
-          builder.append(", ");
-        }
-        process(item, indent);
-        firstItem = false;
-      }
-      builder.append(")");
-      return null;
-    }
-
-    @Override
-    public Void visitCreateFunction(CreateFunction node, Integer indent) {
-      builder
-          .append("CREATE FUNCTION ")
-          .append(node.getUdfName())
-          .append(" AS ")
-          .append(node.getClassName());
-      node.getUriString().ifPresent(uri -> builder.append(" USING URI 
").append(uri));
-      return null;
-    }
-
-    @Override
-    public Void visitDropFunction(DropFunction node, Integer indent) {
-      builder.append("DROP FUNCTION ");
-      builder.append(node.getUdfName());
-      return null;
-    }
-
-    @Override
-    public Void visitLoadTsFile(final LoadTsFile node, final Integer indent) {
-      builder.append("LOAD ");
-      builder.append("\"" + node.getFilePath() + "\"");
-      builder.append(" \n");
-
-      if (!node.getLoadAttributes().isEmpty()) {
-        builder
-            .append("WITH (")
-            .append("\n")
-            .append(
-                node.getLoadAttributes().entrySet().stream()
-                    .map(
-                        entry ->
-                            indentString(1)
-                                + "\""
-                                + entry.getKey()
-                                + "\" = \""
-                                + entry.getValue()
-                                + "\"")
-                    .collect(joining(", " + "\n")))
-            .append(")\n");
-      }
-      return null;
-    }
-
-    @Override
-    public Void visitCreatePipe(CreatePipe node, Integer context) {
-      builder.append("CREATE PIPE ");
-      if (node.hasIfNotExistsCondition()) {
-        builder.append("IF NOT EXISTS ");
-      }
-      builder.append(node.getPipeName());
-      builder.append(" \n");
-
-      if (!node.getSourceAttributes().isEmpty()) {
-        builder
-            .append("WITH SOURCE (")
-            .append("\n")
-            .append(
-                node.getSourceAttributes().entrySet().stream()
-                    .map(
-                        entry ->
-                            indentString(1)
-                                + "\""
-                                + entry.getKey()
-                                + "\" = \""
-                                + entry.getValue()
-                                + "\"")
-                    .collect(joining(", " + "\n")))
-            .append(")\n");
-      }
-
-      if (!node.getProcessorAttributes().isEmpty()) {
-        builder
-            .append("WITH PROCESSOR (")
-            .append("\n")
-            .append(
-                node.getProcessorAttributes().entrySet().stream()
-                    .map(
-                        entry ->
-                            indentString(1)
-                                + "\""
-                                + entry.getKey()
-                                + "\" = \""
-                                + entry.getValue()
-                                + "\"")
-                    .collect(joining(", " + "\n")))
-            .append(")\n");
-      }
-
-      if (!node.getSinkAttributes().isEmpty()) {
-        builder
-            .append("WITH SINK (")
-            .append("\n")
-            .append(
-                node.getSinkAttributes().entrySet().stream()
-                    .map(
-                        entry ->
-                            indentString(1)
-                                + "\""
-                                + entry.getKey()
-                                + "\" = \""
-                                + entry.getValue()
-                                + "\"")
-                    .collect(joining(", " + "\n")))
-            .append(")");
-      }
-
-      return null;
-    }
-
-    @Override
-    public Void visitAlterPipe(AlterPipe node, Integer context) {
-      builder.append("ALTER PIPE ");
-      if (node.hasIfExistsCondition()) {
-        builder.append("IF EXISTS ");
-      }
-      builder.append(node.getPipeName());
-      builder.append(" \n");
-
-      builder
-          .append(node.isReplaceAllExtractorAttributes() ? "REPLACE" : 
"MODIFY")
-          .append(" SOURCE (")
-          .append("\n")
-          .append(
-              node.getExtractorAttributes().entrySet().stream()
-                  .map(
-                      entry ->
-                          indentString(1)
-                              + "\""
-                              + entry.getKey()
-                              + "\" = \""
-                              + entry.getValue()
-                              + "\"")
-                  .collect(joining(", " + "\n")))
-          .append(")\n");
-
-      builder
-          .append(node.isReplaceAllProcessorAttributes() ? "REPLACE" : 
"MODIFY")
-          .append(" PROCESSOR (")
-          .append("\n")
-          .append(
-              node.getProcessorAttributes().entrySet().stream()
-                  .map(
-                      entry ->
-                          indentString(1)
-                              + "\""
-                              + entry.getKey()
-                              + "\" = \""
-                              + entry.getValue()
-                              + "\"")
-                  .collect(joining(", " + "\n")))
-          .append(")\n");
-
-      builder
-          .append(node.isReplaceAllConnectorAttributes() ? "REPLACE" : 
"MODIFY")
-          .append(" SINK (")
-          .append("\n")
-          .append(
-              node.getConnectorAttributes().entrySet().stream()
-                  .map(
-                      entry ->
-                          indentString(1)
-                              + "\""
-                              + entry.getKey()
-                              + "\" = \""
-                              + entry.getValue()
-                              + "\"")
-                  .collect(joining(", " + "\n")))
-          .append(")");
-
-      return null;
-    }
-
-    @Override
-    public Void visitDropPipe(DropPipe node, Integer context) {
-      builder.append("DROP PIPE ");
-      if (node.hasIfExistsCondition()) {
-        builder.append("IF EXISTS ");
-      }
-      builder.append(node.getPipeName());
-
-      return null;
-    }
-
-    @Override
-    public Void visitStartPipe(StartPipe node, Integer context) {
-      builder.append("START PIPE ").append(node.getPipeName());
-
-      return null;
-    }
-
-    @Override
-    public Void visitStopPipe(StopPipe node, Integer context) {
-      builder.append("STOP PIPE ").append(node.getPipeName());
-
-      return null;
-    }
-
-    @Override
-    public Void visitShowPipes(ShowPipes node, Integer context) {
-      builder.append("SHOW PIPES");
-
-      // TODO: consider pipeName and hasWhereClause in node
-
-      return null;
-    }
-
-    @Override
-    public Void visitCreatePipePlugin(CreatePipePlugin node, Integer context) {
-      builder.append("CREATE PIPEPLUGIN ");
-      if (node.hasIfNotExistsCondition()) {
-        builder.append("IF NOT EXISTS ");
-      }
-      builder.append(node.getPluginName());
-      builder.append("\n");
-
-      builder.append("AS \"");
-      builder.append(node.getClassName());
-      builder.append("\"\n");
-
-      builder.append("USING URI \"");
-      builder.append(node.getUriString());
-      builder.append("\"");
-
-      return null;
-    }
-
-    @Override
-    public Void visitDropPipePlugin(DropPipePlugin node, Integer context) {
-      builder.append("DROP PIPEPLUGIN ");
-      if (node.hasIfExistsCondition()) {
-        builder.append("IF EXISTS ");
-      }
-      builder.append(node.getPluginName());
-
-      return null;
-    }
-
-    @Override
-    public Void visitShowPipePlugins(ShowPipePlugins node, Integer context) {
-      builder.append("SHOW PIPEPLUGINS");
-
-      return null;
-    }
-
-    @Override
-    public Void visitCreateTopic(CreateTopic node, Integer context) {
-      builder.append("CREATE TOPIC ");
-      if (node.hasIfNotExistsCondition()) {
-        builder.append("IF NOT EXISTS ");
-      }
-      builder.append(node.getTopicName());
-      builder.append(" \n");
-
-      if (!node.getTopicAttributes().isEmpty()) {
-        builder
-            .append("WITH (")
-            .append("\n")
-            .append(
-                node.getTopicAttributes().entrySet().stream()
-                    .map(
-                        entry ->
-                            indentString(1)
-                                + "\""
-                                + entry.getKey()
-                                + "\" = \""
-                                + entry.getValue()
-                                + "\"")
-                    .collect(joining(", " + "\n")))
-            .append(")\n");
-      }
-
-      return null;
-    }
-
-    @Override
-    public Void visitDropTopic(DropTopic node, Integer context) {
-      builder.append("DROP TOPIC ");
-      if (node.hasIfExistsCondition()) {
-        builder.append("IF EXISTS ");
-      }
-      builder.append(node.getTopicName());
-
-      return null;
-    }
-
-    @Override
-    public Void visitShowTopics(ShowTopics node, Integer context) {
-      if (Objects.isNull(node.getTopicName())) {
-        builder.append("SHOW TOPICS");
-      } else {
-        builder.append("SHOW TOPIC ").append(node.getTopicName());
-      }
-
-      return null;
-    }
-
-    @Override
-    public Void visitShowSubscriptions(ShowSubscriptions node, Integer 
context) {
-      if (Objects.isNull(node.getTopicName())) {
-        builder.append("SHOW SUBSCRIPTIONS");
-      } else {
-        builder.append("SHOW SUBSCRIPTIONS ON ").append(node.getTopicName());
-      }
-
-      return null;
-    }
-
-    @Override
-    public Void visitDropSubscription(DropSubscription node, Integer context) {
-      builder.append("DROP SUBSCRIPTION ");
-      if (node.hasIfExistsCondition()) {
-        builder.append("IF EXISTS ");
-      }
-      builder.append(node.getSubscriptionId());
-
-      return null;
-    }
-
-    @Override
-    public Void visitRelationalAuthorPlan(RelationalAuthorStatement node, 
Integer context) {
-      switch (node.getAuthorType()) {
-        case GRANT_USER_ANY:
-          builder.append(
-              "GRANT "
-                  + node.getPrivilegesString()
-                  + " ON ANY"
-                  + " TO USER "
-                  + node.getUserName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case GRANT_USER_ALL:
-          builder.append(
-              "GRANT ALL TO USER "
-                  + node.getUserName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case GRANT_USER_DB:
-          builder.append(
-              "GRANT "
-                  + node.getPrivilegesString()
-                  + " ON DATABASE "
-                  + node.getDatabase()
-                  + " TO USER "
-                  + node.getUserName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case GRANT_USER_SYS:
-          builder.append(
-              "GRANT "
-                  + node.getPrivilegesString()
-                  + " TO USER "
-                  + node.getUserName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case GRANT_USER_TB:
-          builder.append(
-              "GRANT "
-                  + node.getPrivilegesString()
-                  + " ON TABLE "
-                  + node.getDatabase()
-                  + "."
-                  + node.getTableName()
-                  + " TO USER "
-                  + node.getUserName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case GRANT_ROLE_ANY:
-          builder.append(
-              "GRANT "
-                  + node.getPrivilegesString()
-                  + " ON ANY"
-                  + " TO ROLE "
-                  + node.getRoleName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case GRANT_ROLE_ALL:
-          builder.append(
-              "GRANT ALL TO ROLE "
-                  + node.getRoleName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case GRANT_ROLE_DB:
-          builder.append(
-              "GRANT "
-                  + node.getPrivilegesString()
-                  + " ON DATABASE "
-                  + node.getDatabase()
-                  + " TO ROLE "
-                  + node.getRoleName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case GRANT_ROLE_SYS:
-          builder.append(
-              "GRANT "
-                  + node.getPrivilegesString()
-                  + " TO ROLE "
-                  + node.getRoleName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case GRANT_ROLE_TB:
-          builder.append(
-              "GRANT "
-                  + node.getPrivilegesString()
-                  + " ON TABLE "
-                  + node.getDatabase()
-                  + "."
-                  + node.getTableName()
-                  + " TO ROLE "
-                  + node.getRoleName()
-                  + (node.isGrantOption() ? " WITH GRANT OPTION" : ""));
-          break;
-        case REVOKE_USER_ANY:
-          builder.append(
-              "REVOKE "
-                  + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
-                  + node.getPrivilegesString()
-                  + " ON ANY FROM USER "
-                  + node.getUserName());
-          break;
-        case REVOKE_USER_ALL:
-          builder.append(
-              "REVOKE"
-                  + (node.isGrantOption() ? "GRANT OPTION FOR ALL" : "ALL")
-                  + " FROM USER "
-                  + node.getUserName());
-          break;
-        case REVOKE_USER_DB:
-          builder.append(
-              "REVOKE "
-                  + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
-                  + node.getPrivilegesString()
-                  + " ON DATABASE "
-                  + node.getDatabase()
-                  + " FROM USER "
-                  + node.getUserName());
-          break;
-        case REVOKE_USER_SYS:
-          builder.append(
-              "REVOKE "
-                  + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
-                  + node.getPrivilegesString()
-                  + "FROM USER "
-                  + node.getUserName());
-          break;
-        case REVOKE_USER_TB:
-          builder.append(
-              "REVOKE "
-                  + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
-                  + node.getPrivilegesString()
-                  + " ON TABLE "
-                  + node.getDatabase()
-                  + "."
-                  + node.getTableName()
-                  + " FROM USER "
-                  + node.getUserName());
-          break;
-        case REVOKE_ROLE_ANY:
-          builder.append(
-              "REVOKE "
-                  + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
-                  + node.getPrivilegesString()
-                  + " ON ANY FROM ROLE "
-                  + node.getRoleName());
-          break;
-        case REVOKE_ROLE_ALL:
-          builder.append(
-              "REVOKE "
-                  + (node.isGrantOption() ? "GRANT OPTION FOR ALL" : "ALL")
-                  + " FROM ROLE "
-                  + node.getRoleName());
-          break;
-        case REVOKE_ROLE_DB:
-          builder.append(
-              "REVOKE "
-                  + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
-                  + node.getPrivilegesString()
-                  + " ON DATABASE "
-                  + node.getDatabase()
-                  + " FROM ROLE "
-                  + node.getRoleName());
-          break;
-        case REVOKE_ROLE_SYS:
-          builder.append(
-              "REVOKE "
-                  + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
-                  + node.getPrivilegesString()
-                  + " FROM ROLE "
-                  + node.getRoleName());
-          break;
-        case REVOKE_ROLE_TB:
-          builder.append(
-              "REVOKE "
-                  + (node.isGrantOption() ? "GRANT OPTION FOR " : "")
-                  + node.getPrivilegesString()
-                  + " ON TABLE "
-                  + node.getDatabase()
-                  + "."
-                  + node.getTableName()
-                  + " FROM ROLE "
-                  + node.getRoleName());
-          break;
-        case GRANT_USER_ROLE:
-          builder.append("GRANT ROLE " + node.getRoleName() + " TO " + 
node.getUserName());
-          break;
-        case REVOKE_USER_ROLE:
-          builder.append("REVOKE ROLE " + node.getRoleName() + " FROM " + 
node.getUserName());
-          break;
-        case CREATE_USER:
-          builder.append("CREATE USER " + node.getUserName());
-          break;
-        case CREATE_ROLE:
-          builder.append("CREATE ROLE " + node.getRoleName());
-          break;
-        case UPDATE_USER:
-          builder.append("ALTER USER " + node.getUserName() + " SET PASSWORD");
-          break;
-        case LIST_USER:
-          builder.append("LIST USER ");
-          break;
-        case LIST_ROLE:
-          builder.append("LIST ROLE ");
-          break;
-        case LIST_USER_PRIV:
-          builder.append("LIST PRIVILEGES OF USER " + node.getUserName());
-          break;
-        case LIST_ROLE_PRIV:
-          builder.append("LIST PRIVILEGES OF ROLE " + node.getRoleName());
-          break;
-        case DROP_ROLE:
-          builder.append("DROP ROLE " + node.getRoleName());
-          break;
-        case DROP_USER:
-          builder.append("DROP USER " + node.getUserName());
-          break;
-        default:
-          break;
-      }
-      return null;
-    }
-
-    private void appendBeginLabel(Optional<Identifier> label) {
-      label.ifPresent(value -> builder.append(formatName(value)).append(": "));
-    }
-
-    private void processRelation(Relation relation, Integer indent) {
-      // TODO: handle this properly
-      if (relation instanceof Table) {
-        builder.append("TABLE ").append(formatName(((Table) 
relation).getName())).append('\n');
-      } else {
-        process(relation, indent);
-      }
-    }
-
-    private SqlBuilder append(int indent, String value) {
-      return builder.append(indentString(indent)).append(value);
-    }
-
-    private static String indentString(int indent) {
-      return Strings.repeat(INDENT, indent);
-    }
-
-    private void formatDefinitionList(List<String> elements, int indent) {
-      if (elements.size() == 1) {
-        builder.append(" ").append(getOnlyElement(elements)).append("\n");
-      } else {
-        builder.append("\n");
-        for (int i = 0; i < elements.size() - 1; i++) {
-          append(indent, elements.get(i)).append(",\n");
-        }
-        append(indent, elements.get(elements.size() - 1)).append("\n");
-      }
-    }
-
-    private void appendAliasColumns(Formatter.SqlBuilder builder, 
List<Identifier> columns) {
-      if ((columns != null) && !columns.isEmpty()) {
-        String formattedColumns =
-            columns.stream().map(SqlFormatter::formatName).collect(joining(", 
"));
-
-        builder.append(" (").append(formattedColumns).append(')');
-      }
-    }
-
-    @Override
-    public Void visitTableFunctionInvocation(TableFunctionInvocation node, 
Integer indent) {
-      append(indent, "TABLE(");
-      appendTableFunctionInvocation(node, indent + 1);
-      builder.append(")");
-      return null;
-    }
-
-    private void appendTableFunctionInvocation(TableFunctionInvocation node, 
Integer indent) {
-      builder.append(formatName(node.getName())).append("(\n");
-      appendTableFunctionArguments(node.getArguments(), indent + 1);
-      builder.append(")");
-    }
-
-    private void appendTableFunctionArguments(List<TableFunctionArgument> 
arguments, int indent) {
-      for (int i = 0; i < arguments.size(); i++) {
-        TableFunctionArgument argument = arguments.get(i);
-        if (argument.getName().isPresent()) {
-          append(indent, formatName(argument.getName().get()));
-          builder.append(" => ");
-        } else {
-          append(indent, "");
-        }
-        Node value = argument.getValue();
-        if (value instanceof Expression) {
-          builder.append(formatExpression((Expression) value));
-        } else {
-          process(value, indent + 1);
-        }
-        if (i < arguments.size() - 1) {
-          builder.append(",\n");
-        }
-      }
-    }
-
-    @Override
-    public Void visitTableArgument(TableFunctionTableArgument node, Integer 
indent) {
-      Relation relation = node.getTable();
-      Node unaliased =
-          relation instanceof AliasedRelation
-              ? ((AliasedRelation) relation).getRelation()
-              : relation;
-      if (unaliased instanceof TableSubquery) {
-        // unpack the relation from TableSubquery to avoid adding another pair 
of parentheses
-        unaliased = ((TableSubquery) unaliased).getQuery();
-      }
-      builder.append("TABLE(");
-      process(unaliased, indent);
-      builder.append(")");
-      if (relation instanceof AliasedRelation) {
-        AliasedRelation aliasedRelation = (AliasedRelation) relation;
-        builder.append(" AS ").append(formatName(aliasedRelation.getAlias()));
-        appendAliasColumns(builder, aliasedRelation.getColumnNames());
-      }
-      if (node.getPartitionBy().isPresent()) {
-        builder.append("\n");
-        append(indent, "PARTITION BY ")
-            .append(
-                node.getPartitionBy().get().stream()
-                    .map(SqlFormatter::formatExpression)
-                    .collect(joining(", ")));
-      }
-      node.getOrderBy()
-          .ifPresent(
-              orderBy -> {
-                builder.append("\n");
-                append(indent, formatOrderBy(orderBy));
-              });
-
-      return null;
+    @CanIgnoreReturnValue
+    public SqlBuilder append(char c) {
+      builder.append(c);
+      return this;
     }
   }
 }

Reply via email to