/* * Copyright 1999-2017 Alibaba Group Holding Ltd. * * Licensed 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 com.alibaba.druid.sql.dialect.odps.visitor; import java.util.HashSet; import java.util.List; import java.util.Set; import com.alibaba.druid.sql.ast.SQLDataType; import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.statement.*; import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource.JoinType; import com.alibaba.druid.sql.dialect.odps.ast.*; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; import com.alibaba.druid.util.JdbcConstants; public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVisitor { private Set<String> builtInFunctions = new HashSet<String>(); { builtInFunctions.add("IF"); builtInFunctions.add("COALESCE"); builtInFunctions.add("TO_DATE"); builtInFunctions.add("SUBSTR"); builtInFunctions.add("INSTR"); builtInFunctions.add("LENGTH"); builtInFunctions.add("SPLIT"); builtInFunctions.add("TOLOWER"); builtInFunctions.add("TOUPPER"); builtInFunctions.add("EXPLODE"); builtInFunctions.add("LEAST"); builtInFunctions.add("GREATEST"); groupItemSingleLine = true; } public OdpsOutputVisitor(Appendable appender){ super(appender, JdbcConstants.ODPS); } public boolean visit(OdpsCreateTableStatement x) { if (x.isIfNotExiists()) { print0(ucase ? "CREATE TABLE IF NOT EXISTS " : "create table if not exists "); } else { print0(ucase ? "CREATE TABLE " : "create table "); } x.getName().accept(this); if (x.getLike() != null) { print0(ucase ? " LIKE " : " like "); x.getLike().accept(this); } int size = x.getTableElementList().size(); if (size > 0) { print0(" ("); if (this.isPrettyFormat() && x.hasBodyBeforeComment()) { print(' '); printComment(x.getBodyBeforeCommentsDirect(), ""); } incrementIndent(); println(); for (int i = 0; i < size; ++i) { SQLTableElement element = x.getTableElementList().get(i); element.accept(this); if (i != size - 1) { print(','); } if (this.isPrettyFormat() && element.hasAfterComment()) { print(' '); printComment(element.getAfterCommentsDirect(), "\n"); } if (i != size - 1) { println(); } } decrementIndent(); println(); print(')'); } if (x.getComment() != null) { println(); print0(ucase ? "COMMENT " : "comment "); x.getComment().accept(this); } int partitionSize = x.getPartitionColumns().size(); if (partitionSize > 0) { println(); print0(ucase ? "PARTITIONED BY (" : "partitioned by ("); incrementIndent(); println(); for (int i = 0; i < partitionSize; ++i) { SQLColumnDefinition column = x.getPartitionColumns().get(i); column.accept(this); if (i != partitionSize - 1) { print(','); } if (this.isPrettyFormat() && column.hasAfterComment()) { print(' '); printComment(column.getAfterCommentsDirect(), "\n"); } if (i != partitionSize - 1) { println(); } } decrementIndent(); println(); print(')'); } if (x.getLifecycle() != null) { println(); print0(ucase ? "LIFECYCLE " : "lifecycle "); x.getLifecycle().accept(this); } if (x.getSelect() != null) { println(); print0(ucase ? "AS" : "as"); println(); x.getSelect().accept(this); } return false; } @Override public void endVisit(OdpsCreateTableStatement x) { super.endVisit((SQLCreateTableStatement) x); } public SQLStatement parseInsert() { OdpsInsertStatement stmt = new OdpsInsertStatement(); return stmt; } @Override public void endVisit(OdpsInsertStatement x) { } @Override public boolean visit(OdpsInsertStatement x) { SQLTableSource from = x.getFrom(); if (x.getFrom() != null) { if (from instanceof SQLSubqueryTableSource) { SQLSelect select = ((SQLSubqueryTableSource) from).getSelect(); print0(ucase ? "FROM (" : "from ("); incrementIndent(); println(); select.accept(this); decrementIndent(); println(); print0(") "); print0(x.getFrom().getAlias()); } else { print0(ucase ? "FROM " : "from "); from.accept(this); } println(); } for (int i = 0; i < x.getItems().size(); ++i) { OdpsInsert insert = x.getItems().get(i); if (i != 0) { println(); } insert.accept(this); } return false; } @Override public void endVisit(OdpsInsert x) { } @Override public boolean visit(OdpsInsert x) { if (x.hasBeforeComment()) { printComment(x.getBeforeCommentsDirect(), "\n"); println(); } if (x.isOverwrite()) { print0(ucase ? "INSERT OVERWRITE TABLE " : "insert overwrite table "); } else { print0(ucase ? "INSERT INTO TABLE " : "insert into table "); } x.getTableSource().accept(this); int partitions = x.getPartitions().size(); if (partitions > 0) { print0(ucase ? " PARTITION (" : " partition ("); for (int i = 0; i < partitions; ++i) { if (i != 0) { print0(", "); } SQLAssignItem assign = x.getPartitions().get(i); assign.getTarget().accept(this); if (assign.getValue() != null) { print('='); assign.getValue().accept(this); } } print(')'); } println(); x.getQuery().accept(this); return false; } public boolean visit(SQLCaseExpr x) { incrementIndent(); print0(ucase ? "CASE " : "case "); if (x.getValueExpr() != null) { x.getValueExpr().accept(this); println(); } for (int i = 0, size = x.getItems().size(); i < size; ++i) { println(); x.getItems().get(i).accept(this); } if (x.getElseExpr() != null) { println(); print0(ucase ? "ELSE " : "else "); x.getElseExpr().accept(this); } decrementIndent(); println(); print0(ucase ? "END" : "end"); return false; } protected void printSelectList(List<SQLSelectItem> selectList) { incrementIndent(); for (int i = 0, size = selectList.size(); i < size; ++i) { SQLSelectItem selectItem = selectList.get(i); if (i != 0) { SQLSelectItem preSelectItem = selectList.get(i - 1); if (preSelectItem.hasAfterComment()) { print(' '); printComment(preSelectItem.getAfterCommentsDirect(), "\n"); } println(); print0(", "); } selectItem.accept(this); if (i == selectList.size() - 1 && selectItem.hasAfterComment()) { print(' '); printComment(selectItem.getAfterCommentsDirect(), "\n"); } } decrementIndent(); } @Override public boolean visit(SQLSubqueryTableSource x) { print('('); incrementIndent(); println(); x.getSelect().accept(this); decrementIndent(); println(); print(')'); if (x.getAlias() != null) { print(' '); print0(x.getAlias()); } return false; } @Override public boolean visit(SQLJoinTableSource x) { x.getLeft().accept(this); if (x.getJoinType() == JoinType.COMMA) { print(','); } else { println(); printJoinType(x.getJoinType()); } print(' '); x.getRight().accept(this); if (x.getCondition() != null) { println(); print0(ucase ? "ON " : "on "); incrementIndent(); x.getCondition().accept(this); decrementIndent(); } if (x.getUsing().size() > 0) { print0(ucase ? " USING (" : " using ("); printAndAccept(x.getUsing(), ", "); print(')'); } if (x.getAlias() != null) { print0(ucase ? " AS " : " as "); print0(x.getAlias()); } return false; } @Override public void endVisit(OdpsUDTFSQLSelectItem x) { } @Override public boolean visit(OdpsUDTFSQLSelectItem x) { x.getExpr().accept(this); println(); print0(ucase ? "AS (" : "as ("); int aliasSize = x.getAliasList().size(); if (aliasSize > 5) { incrementIndent(); println(); } for (int i = 0; i < aliasSize; ++i) { if (i != 0) { if (aliasSize > 5) { println(","); } else { print0(", "); } } print0(x.getAliasList().get(i)); } if (aliasSize > 5) { decrementIndent(); println(); } print(')'); return false; } @Override public void endVisit(OdpsShowPartitionsStmt x) { } @Override public boolean visit(OdpsShowPartitionsStmt x) { print0(ucase ? "SHOW PARTITIONS " : "show partitions "); x.getTableSource().accept(this); return false; } @Override public void endVisit(OdpsShowStatisticStmt x) { } @Override public boolean visit(OdpsShowStatisticStmt x) { print0(ucase ? "SHOW STATISTIC " : "show statistic "); x.getTableSource().accept(this); return false; } @Override public void endVisit(OdpsSetLabelStatement x) { } @Override public boolean visit(OdpsSetLabelStatement x) { print0(ucase ? "SET LABEL " : "set label "); print0(x.getLabel()); print0(ucase ? " TO " : " to "); if (x.getUser() != null) { print0(ucase ? "USER " : "user "); x.getUser().accept(this); } else if (x.getTable() != null) { print0(ucase ? "TABLE " : "table "); x.getTable().accept(this); if (x.getColumns().size() > 0) { print('('); printAndAccept(x.getColumns(), ", "); print(')'); } } return false; } @Override public void endVisit(OdpsSelectQueryBlock x) { } @Override public boolean visit(OdpsSelectQueryBlock x) { if (isPrettyFormat() && x.hasBeforeComment()) { printComment(x.getBeforeCommentsDirect(), "\n"); println(); } print0(ucase ? "SELECT " : "select "); List<SQLHint> hints = x.getHintsDirect(); if (hints != null) { printAndAccept(hints, " "); print(' '); } if (SQLSetQuantifier.ALL == x.getDistionOption()) { print0(ucase ? "ALL " : "all "); } else if (SQLSetQuantifier.DISTINCT == x.getDistionOption()) { print0(ucase ? "DISTINCT " : "distinct "); } else if (SQLSetQuantifier.UNIQUE == x.getDistionOption()) { print0(ucase ? "UNIQUE " : "unique "); } printSelectList(x.getSelectList()); if (x.getFrom() != null) { println(); print0(ucase ? "FROM " : "from "); x.getFrom().accept(this); } if (x.getWhere() != null) { println(); print0(ucase ? "WHERE " : "where "); if (x.getWhere().hasBeforeComment() && isPrettyFormat()) { printlnComments(x.getWhere().getBeforeCommentsDirect()); } x.getWhere().setParent(x); x.getWhere().accept(this); if (x.getWhere().hasAfterComment() && isPrettyFormat()) { print(' '); printComment(x.getWhere().getAfterCommentsDirect(), "\n"); } } if (x.getGroupBy() != null) { println(); x.getGroupBy().accept(this); } if (x.getOrderBy() != null) { println(); x.getOrderBy().accept(this); } if (x.getDistributeBy().size() > 0) { println(); print0(ucase ? "DISTRIBUTE BY " : "distribute by "); printAndAccept(x.getDistributeBy(), ", "); if (!x.getSortBy().isEmpty()) { print0(ucase ? " SORT BY " : " sort by "); printAndAccept(x.getSortBy(), ", "); } } if (x.getLimit() != null) { println(); x.getLimit().accept(this); } return false; } public boolean visit(SQLOrderBy x) { int itemSize = x.getItems().size(); if (itemSize > 0) { print0(ucase ? "ORDER BY " : "order by "); incrementIndent(); for (int i = 0; i < itemSize; ++i) { if (i != 0) { println(", "); } x.getItems().get(i).accept(this); } decrementIndent(); } return false; } @Override public void endVisit(OdpsAnalyzeTableStatement x) { } @Override public boolean visit(OdpsAnalyzeTableStatement x) { print0(ucase ? "ANALYZE TABLE " : "analyze table "); x.getTable().accept(this); if (x.getPartition().size() > 0) { print0(ucase ? " PARTITION (" : " partition ("); printAndAccept(x.getPartition(), ", "); print(')'); } print0(ucase ? " COMPUTE STATISTICS" : " compute statistics"); return false; } @Override public void endVisit(OdpsAddStatisticStatement x) { } @Override public boolean visit(OdpsAddStatisticStatement x) { print0(ucase ? "ADD STATISTIC " : "add statistic "); x.getTable().accept(this); print(' '); x.getStatisticClause().accept(this); return false; } @Override public void endVisit(OdpsRemoveStatisticStatement x) { } @Override public boolean visit(OdpsRemoveStatisticStatement x) { print0(ucase ? "REMOVE STATISTIC " : "remove statistic "); x.getTable().accept(this); print(' '); x.getStatisticClause().accept(this); return false; } @Override public void endVisit(OdpsStatisticClause.TableCount x) { } @Override public boolean visit(OdpsStatisticClause.TableCount x) { print0(ucase ? "TABLE_COUNT" : "table_count"); return false; } @Override public void endVisit(OdpsStatisticClause.ExpressionCondition x) { } @Override public boolean visit(OdpsStatisticClause.ExpressionCondition x) { print0(ucase ? "EXPRESSION_CONDITION " : "expression_condition "); x.getExpr().accept(this); return false; } @Override public void endVisit(OdpsStatisticClause.NullValue x) { } @Override public boolean visit(OdpsStatisticClause.NullValue x) { print0(ucase ? "NULL_VALUE " : "null_value "); x.getColumn().accept(this); return false; } @Override public void endVisit(OdpsStatisticClause.ColumnSum x) { } @Override public boolean visit(OdpsStatisticClause.ColumnSum x) { print0(ucase ? "COLUMN_SUM " : "column_sum "); x.getColumn().accept(this); return false; } @Override public void endVisit(OdpsStatisticClause.ColumnMax x) { } @Override public boolean visit(OdpsStatisticClause.ColumnMax x) { print0(ucase ? "COLUMN_MAX " : "column_max "); x.getColumn().accept(this); return false; } @Override public void endVisit(OdpsStatisticClause.ColumnMin x) { } @Override public boolean visit(OdpsStatisticClause.ColumnMin x) { print0(ucase ? "COLUMN_MIN " : "column_min "); x.getColumn().accept(this); return false; } @Override public void endVisit(OdpsReadStatement x) { } @Override public boolean visit(OdpsReadStatement x) { print0(ucase ? "READ " : "read "); x.getTable().accept(this); if (x.getColumns().size() > 0) { print0(" ("); printAndAccept(x.getColumns(), ", "); print(')'); } if (x.getPartition().size() > 0) { print0(ucase ? " PARTITION (" : " partition ("); printAndAccept(x.getPartition(), ", "); print(')'); } if (x.getRowCount() != null) { print(' '); x.getRowCount().accept(this); } return false; } public boolean visit(SQLMethodInvokeExpr x) { if (x.getOwner() != null) { x.getOwner().accept(this); print(':'); } printFunctionName(x.getMethodName()); print('('); printAndAccept(x.getParameters(), ", "); print(')'); return false; } protected void printJoinType(JoinType joinType) { if (joinType.equals(JoinType.LEFT_OUTER_JOIN)) { print0(ucase ? "LEFT OUTER JOIN" : "left outer join"); } else if (joinType.equals(JoinType.RIGHT_OUTER_JOIN)) { print0(ucase ? "RIGHT OUTER JOIN" : "right outer join"); } else if (joinType.equals(JoinType.FULL_OUTER_JOIN)) { print0(ucase ? "FULL OUTER JOIN" : "full outer join"); } else { print0(ucase ? joinType.name : joinType.name_lcase); } } public boolean visit(SQLDataType x) { String dataTypeName = x.getName(); print0(ucase ? dataTypeName.toUpperCase() : dataTypeName.toLowerCase()); if (x.getArguments().size() > 0) { print('('); printAndAccept(x.getArguments(), ", "); print(')'); } return false; } protected void printFunctionName(String name) { String upperName = name.toUpperCase(); if (builtInFunctions.contains(upperName)) { print0(ucase ? upperName : name); } else { print0(name); } } @Override public void endVisit(OdpsShowGrantsStmt x) { } @Override public boolean visit(OdpsShowGrantsStmt x) { print0(ucase ? "SHOW GRANTS" : "show grants"); if (x.getUser() != null) { print0(ucase ? " FOR " : " for "); x.getUser().accept(this); } if (x.getObjectType() != null) { print0(ucase ? " ON TYPE " : " on type "); x.getObjectType().accept(this); } return false; } @Override public void endVisit(OdpsListStmt x) { } @Override public boolean visit(OdpsListStmt x) { print0(ucase ? "LIST " : "list "); if (x.getObject() != null) { x.getObject().accept(this); } return false; } @Override public void endVisit(OdpsGrantStmt x) { } @Override public boolean visit(OdpsGrantStmt x) { print0(ucase ? "GRANT " : "grant "); if (x.isSuper()) { print0(ucase ? "SUPER " : "super "); } if (x.isLabel()) { print0(ucase ? "LABEL " : "label "); x.getLabel().accept(this);; } else { printAndAccept(x.getPrivileges(), ", "); } if (x.getOn() != null) { print0(ucase ? " ON " : " on "); if (x.getObjectType() != null) { print0(ucase ? x.getObjectType().name() : x.getObjectType().name().toLowerCase()); print(' '); } x.getOn().accept(this); if (x.getColumns().size() > 0) { print('('); printAndAccept(x.getColumns(), ", "); print(')'); } } if (x.getTo() != null) { print0(ucase ? " TO " : " to "); if (x.getSubjectType() != null) { print0(x.getSubjectType().name()); print(' '); } x.getTo().accept(this); } if (x.getExpire() != null) { print0(ucase ? " WITH EXP " : " with exp "); x.getExpire().accept(this); } return false; } @Override public void endVisit(OdpsLateralViewTableSource x) { } @Override public boolean visit(OdpsLateralViewTableSource x) { x.getTableSource().accept(this); incrementIndent(); println(); print0(ucase ? "LATERAL VIEW " : "lateral view "); x.getMethod().accept(this); print(' '); print0(x.getAlias()); print0(ucase ? " AS " : " as "); printAndAccept(x.getColumns(), ", "); decrementIndent(); return false; } public boolean visit(SQLCharExpr x) { String text = x.getText(); if (text == null) { print0(ucase ? "NULL" : "null"); } else { StringBuilder buf = new StringBuilder(text.length() + 2); buf.append('\''); for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); switch (ch) { case '\\': buf.append("\\\\"); break; case '\'': buf.append("\\'"); break; case '\0': buf.append("\\0"); break; case '\n': buf.append("\\n"); break; default: buf.append(ch); break; } } buf.append('\''); print0(buf.toString()); } return false; } @Override public void endVisit(OdpsValuesTableSource x) { } @Override public boolean visit(OdpsValuesTableSource x) { print0(ucase ? "VALUES " : "values "); printAndAccept(x.getValues(), ", "); print(' '); print0(x.getAlias()); print0(" ("); printAndAccept(x.getColumns(), ", "); print(')'); return false; } @Override public boolean visit(SQLAlterTableRenameColumn x) { print0(ucase ? "CHANGE COLUMN " : "change column "); x.getColumn().accept(this); print0(ucase ? " RENAME TO " : " rename to "); x.getTo().accept(this); return false; } }