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;
}
}
}