diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLHint.java b/src/main/java/com/alibaba/druid/sql/ast/SQLHint.java new file mode 100644 index 0000000000..538d0a837e --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLHint.java @@ -0,0 +1,6 @@ +package com.alibaba.druid.sql.ast; + + +public interface SQLHint extends SQLObject { + +} diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLJoinTableSource.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLJoinTableSource.java index 9120167951..0668977fcf 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLJoinTableSource.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLJoinTableSource.java @@ -91,24 +91,24 @@ public class SQLJoinTableSource extends SQLTableSourceImpl { } public static enum JoinType { - COMMA, JOIN, INNER_JOIN, CROSS_JOIN, NATURAL_JOIN, NATURAL_INNER_JOIN, LEFT_OUTER_JOIN, RIGHT_OUTER_JOIN, - FULL_OUTER_JOIN; + COMMA(","), // + JOIN("JOIN"), // + INNER_JOIN("INNER JOIN"), // + CROSS_JOIN("CROSS JOIN"), // + NATURAL_JOIN("NATURAL JOIN"), // + NATURAL_INNER_JOIN("NATURAL INNER JOIN"), // + LEFT_OUTER_JOIN("LEFT JOIN"), + RIGHT_OUTER_JOIN("RIGHT JOIN"), + FULL_OUTER_JOIN("FULL JOIN"), + STRAIGHT_JOIN("STRAIGHT_JOIN"); + public final String name; + + JoinType(String name){ + this.name = name; + } public static String toString(JoinType joinType) { - if (JOIN.equals(joinType)) return "JOIN"; - if (INNER_JOIN.equals(joinType)) return "INNER JOIN"; - if (CROSS_JOIN.equals(joinType)) return "CROSS JOIN"; - if (NATURAL_JOIN.equals(joinType)) return "NATURAL JOIN"; - if (NATURAL_INNER_JOIN.equals(joinType)) return "NATURAL INNER JOIN"; - if (LEFT_OUTER_JOIN.equals(joinType)) return "LEFT JOIN"; - if (RIGHT_OUTER_JOIN.equals(joinType)) return "RIGHT JOIN"; - if (FULL_OUTER_JOIN.equals(joinType)) { - return "FULL JOIN"; - } - if (COMMA.equals(joinType)) { - return ","; - } - throw new IllegalStateException("not supported join " + joinType); + return joinType.name; } } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLTableSource.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLTableSource.java index d66f7a0520..16f2864375 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLTableSource.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLTableSource.java @@ -1,5 +1,8 @@ package com.alibaba.druid.sql.ast.statement; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.SQLObject; public interface SQLTableSource extends SQLObject { @@ -7,4 +10,6 @@ public interface SQLTableSource extends SQLObject { String getAlias(); void setAlias(String alias); + + List getHints(); } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLTableSourceImpl.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLTableSourceImpl.java index 6ee8152ece..cf700cff3d 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLTableSourceImpl.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLTableSourceImpl.java @@ -15,6 +15,10 @@ */ package com.alibaba.druid.sql.ast.statement; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.SQLObjectImpl; public abstract class SQLTableSourceImpl extends SQLObjectImpl implements SQLTableSource { @@ -23,6 +27,8 @@ public abstract class SQLTableSourceImpl extends SQLObjectImpl implements SQLTab protected String alias; + protected List hints = new ArrayList(2); + public SQLTableSourceImpl(){ } @@ -39,4 +45,13 @@ public abstract class SQLTableSourceImpl extends SQLObjectImpl implements SQLTab public void setAlias(String alias) { this.alias = alias; } + + public List getHints() { + return hints; + } + + public void setHints(List hints) { + this.hints = hints; + } + } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionOperator.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionOperator.java index 7095772754..ec9be84290 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionOperator.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionOperator.java @@ -1,7 +1,7 @@ package com.alibaba.druid.sql.ast.statement; public enum SQLUnionOperator { - UNION("UNION"), UNION_ALL("UNION ALL"), MINUS("MINUS"), INTERSECT("INTERSECT"); + UNION("UNION"), UNION_ALL("UNION ALL"), MINUS("MINUS"), INTERSECT("INTERSECT"), DISTINCT("UNION DISTINCT"); public final String name; diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQuery.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQuery.java index ecb50bc3bb..20fe042c34 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQuery.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQuery.java @@ -68,6 +68,7 @@ public class SQLUnionQuery extends SQLSelectQuery { if (visitor.visit(this)) { acceptChild(visitor, left); acceptChild(visitor, right); + acceptChild(visitor, orderBy); } visitor.endVisit(this); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlHint.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlHint.java new file mode 100644 index 0000000000..3e9355750a --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlHint.java @@ -0,0 +1,8 @@ +package com.alibaba.druid.sql.dialect.mysql.ast; + +import com.alibaba.druid.sql.ast.SQLHint; + + +public interface MySqlHint extends SQLHint, MySqlObject { + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlIndexHint.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlIndexHint.java new file mode 100644 index 0000000000..bc7b86737b --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlIndexHint.java @@ -0,0 +1,16 @@ +package com.alibaba.druid.sql.dialect.mysql.ast; + +public interface MySqlIndexHint extends MySqlHint { + public static enum Option { + JOIN("JOIN"), + ORDER_BY("ORDER BY"), + GROUP_BY("GROUP BY") + ; + + public final String name; + + Option(String name) { + this.name = name; + } + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlIndexHintImpl.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlIndexHintImpl.java new file mode 100644 index 0000000000..ed87883994 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlIndexHintImpl.java @@ -0,0 +1,36 @@ +package com.alibaba.druid.sql.dialect.mysql.ast; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; + +public abstract class MySqlIndexHintImpl extends MySqlObjectImpl implements MySqlIndexHint { + + private static final long serialVersionUID = 1L; + + private MySqlIndexHint.Option option; + + private List indexList = new ArrayList(); + + @Override + public abstract void accept0(MySqlASTVisitor visitor); + + public MySqlIndexHint.Option getOption() { + return option; + } + + public void setOption(MySqlIndexHint.Option option) { + this.option = option; + } + + public List getIndexList() { + return indexList; + } + + public void setIndexList(List indexList) { + this.indexList = indexList; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlUseIndexHint.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlUseIndexHint.java new file mode 100644 index 0000000000..c74f06b95e --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlUseIndexHint.java @@ -0,0 +1,17 @@ +package com.alibaba.druid.sql.dialect.mysql.ast; + +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; + +public class MySqlUseIndexHint extends MySqlIndexHintImpl { + + private static final long serialVersionUID = 1L; + + @Override + public void accept0(MySqlASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, getIndexList()); + } + visitor.endVisit(this); + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnionQuery.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnionQuery.java new file mode 100644 index 0000000000..70032e1361 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnionQuery.java @@ -0,0 +1,41 @@ +package com.alibaba.druid.sql.dialect.mysql.ast.statement; + +import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class MySqlUnionQuery extends SQLUnionQuery implements MySqlStatement { + + private static final long serialVersionUID = 1L; + + private Limit limit; + + public Limit getLimit() { + return limit; + } + + public void setLimit(Limit limit) { + this.limit = limit; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + if (visitor instanceof MySqlASTVisitor) { + accept0((MySqlASTVisitor) visitor); + } else { + throw new IllegalArgumentException("not support visitor type : " + visitor.getClass().getName()); + } + } + + @Override + public void accept0(MySqlASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, getLeft()); + acceptChild(visitor, getRight()); + acceptChild(visitor, getOrderBy()); + acceptChild(visitor, getLimit()); + } + visitor.endVisit(this); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java index 1e60def916..42b234cfe0 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java @@ -23,11 +23,13 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlIndexHint; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectGroupBy; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; -import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnionQuery; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnionQuery; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLSelectParser; @@ -249,19 +251,55 @@ public class MySqlSelectParser extends SQLSelectParser { protected MySqlExprParser createExprParser() { return new MySqlExprParser(lexer); } - + protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) throws ParserException { if (identifierEquals("USING")) { return tableSource; } - + + if (identifierEquals("USE")) { + lexer.nextToken(); + MySqlUseIndexHint hint = new MySqlUseIndexHint(); + if (lexer.token() == Token.INDEX) { + lexer.nextToken(); + } else { + accept(Token.KEY); + } + + if (lexer.token() == Token.FOR) { + lexer.nextToken(); + + if (lexer.token() == Token.JOIN) { + lexer.nextToken(); + hint.setOption(MySqlIndexHint.Option.JOIN); + } else if (lexer.token() == Token.ORDER) { + lexer.nextToken(); + accept(Token.BY); + hint.setOption(MySqlIndexHint.Option.ORDER_BY); + } else { + accept(Token.GROUP); + accept(Token.BY); + hint.setOption(MySqlIndexHint.Option.GROUP_BY); + } + } + + accept(Token.LPAREN); + this.createExprParser().names(hint.getIndexList()); + accept(Token.RPAREN); + tableSource.getHints().add(hint); + } + + if (identifierEquals("IGNORE")) { + throw new ParserException("TODO"); + } + return super.parseTableSourceRest(tableSource); } protected MySqlUnionQuery createSQLUnionQuery() { return new MySqlUnionQuery(); } - + public SQLUnionQuery unionRest(SQLUnionQuery union) { if (lexer.token() == Token.LIMIT) { MySqlUnionQuery mysqlUnionQuery = (MySqlUnionQuery) union; @@ -269,7 +307,7 @@ public class MySqlSelectParser extends SQLSelectParser { } return super.unionRest(union); } - + public Limit parseLimit() { return this.createExprParser().parseLimit(); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java index c25bdae87f..36f8444a57 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java @@ -17,6 +17,7 @@ package com.alibaba.druid.sql.dialect.mysql.visitor; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; @@ -97,6 +98,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnionQuery; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -445,4 +447,12 @@ public interface MySqlASTVisitor extends SQLASTVisitor { boolean visit(MySqlDropViewStatement x); void endVisit(MySqlDropViewStatement x); + + boolean visit(MySqlUnionQuery x); + + void endVisit(MySqlUnionQuery x); + + boolean visit(MySqlUseIndexHint x); + + void endVisit(MySqlUseIndexHint x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java index 54a05763c4..ccb5d24152 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java @@ -2,6 +2,7 @@ package com.alibaba.druid.sql.dialect.mysql.visitor; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; @@ -84,6 +85,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnionQuery; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter; @@ -969,4 +971,24 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements public void endVisit(MySqlDropViewStatement x) { } + + @Override + public boolean visit(MySqlUnionQuery x) { + return true; + } + + @Override + public void endVisit(MySqlUnionQuery x) { + + } + + @Override + public boolean visit(MySqlUseIndexHint x) { + return true; + } + + @Override + public void endVisit(MySqlUseIndexHint x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index a0042cbbb7..5040a76118 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -29,10 +29,12 @@ import com.alibaba.druid.sql.ast.statement.SQLCharactorDataType; import com.alibaba.druid.sql.ast.statement.SQLColumnConstraint; import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; @@ -118,6 +120,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnionQuery; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; @@ -1350,7 +1353,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public boolean visit(MySqlDropTableStatement x) { if (x.isTemporary()) { - print("DROP TEMPORARY TABLE "); + print("DROP TEMPORARY TABLE "); } else { print("DROP TABLE "); } @@ -1359,14 +1362,14 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } printAndAccept(x.getTableSources(), ", "); - + if (x.getOption() != null) { print(' '); print(x.getOption()); } return false; } - + @Override public void endVisit(MySqlPartitionByKey x) { @@ -2221,16 +2224,16 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV print(x.getType()); print(" "); } - + print("INDEX "); - + x.getName().accept(this); print(" ON "); x.getTable().accept(this); print(" ("); printAndAccept(x.getItems(), ", "); print(")"); - + if (x.getUsing() != null) { print(" USING "); print(x.getUsing()); @@ -2240,7 +2243,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public void endVisit(MySqlCreateIndexStatement x) { - + } @Override @@ -2253,7 +2256,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public void endVisit(MySqlRenameTableStatement.Item x) { - + } @Override @@ -2265,7 +2268,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public void endVisit(MySqlRenameTableStatement x) { - + } @Override @@ -2274,9 +2277,9 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV if (x.isIfExists()) { print("IF EXISTS "); } - + printAndAccept(x.getTableSources(), ", "); - + if (x.getOption() != null) { print(' '); print(x.getOption()); @@ -2286,7 +2289,100 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public void endVisit(MySqlDropViewStatement x) { + + } + + @Override + public boolean visit(MySqlUnionQuery x) { + { + boolean needParen = false; + if (x.getLeft() instanceof MySqlSelectQueryBlock) { + MySqlSelectQueryBlock right = (MySqlSelectQueryBlock) x.getLeft(); + if (right.getOrderBy() != null || right.getLimit() != null) { + needParen = true; + } + } + if (needParen) { + print('('); + x.getLeft().accept(this); + print(')'); + } else { + x.getLeft().accept(this); + } + } + println(); + print(x.getOperator().name); + println(); + + boolean needParen = false; + + if (x.getOrderBy() != null || x.getLimit() != null) { + needParen = true; + } else if (x.getRight() instanceof MySqlSelectQueryBlock) { + MySqlSelectQueryBlock right = (MySqlSelectQueryBlock) x.getRight(); + if (right.getOrderBy() != null || right.getLimit() != null) { + needParen = true; + } + } + + if (needParen) { + print('('); + x.getRight().accept(this); + print(')'); + } else { + x.getRight().accept(this); + } + + if (x.getOrderBy() != null) { + println(); + x.getOrderBy().accept(this); + } + + if (x.getLimit() != null) { + println(); + x.getLimit().accept(this); + } + + return false; + } + + @Override + public void endVisit(MySqlUnionQuery x) { + + } + + @Override + public boolean visit(MySqlUseIndexHint x) { + print("USE INDEX "); + if (x.getOption() != null) { + print("FOR "); + print(x.getOption().name); + print(' '); + } + print('('); + printAndAccept(x.getIndexList(), ", "); + print(')'); + return false; + } + + @Override + public void endVisit(MySqlUseIndexHint x) { } + public boolean visit(SQLExprTableSource x) { + x.getExpr().accept(this); + + if (x.getAlias() != null) { + print(' '); + print(x.getAlias()); + } + + for (int i = 0; i < x.getHints().size(); ++i) { + print(' '); + x.getHints().get(i).accept(this); + } + + return false; + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java index 93aab11ae5..2b4966a1d1 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java @@ -8,9 +8,11 @@ import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; @@ -93,6 +95,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnionQuery; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.visitor.SchemaStatVisitor; import com.alibaba.druid.stat.TableStat; @@ -1032,4 +1035,24 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS public void endVisit(MySqlDropViewStatement x) { } + + @Override + public boolean visit(MySqlUnionQuery x) { + return visit((SQLUnionQuery) x); + } + + @Override + public void endVisit(MySqlUnionQuery x) { + + } + + @Override + public boolean visit(MySqlUseIndexHint x) { + return false; + } + + @Override + public void endVisit(MySqlUseIndexHint x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleHint.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleHint.java index 0e71605dde..1ffc5606c4 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleHint.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleHint.java @@ -15,9 +15,10 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; -public class OracleHint extends OracleSQLObjectImpl { +public class OracleHint extends OracleSQLObjectImpl implements SQLHint { private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleDeleteStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleDeleteStatement.java index 2653cc1f3a..86b3f55a84 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleDeleteStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleDeleteStatement.java @@ -18,8 +18,8 @@ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; import java.util.ArrayList; import java.util.List; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleReturningClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -30,7 +30,7 @@ public class OracleDeleteStatement extends SQLDeleteStatement { private boolean only = false; - private final List hints = new ArrayList(); + private final List hints = new ArrayList(); private OracleReturningClause returning = null; public OracleDeleteStatement(){ @@ -45,7 +45,7 @@ public class OracleDeleteStatement extends SQLDeleteStatement { this.returning = returning; } - public List getHints() { + public List getHints() { return this.hints; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleInsertStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleInsertStatement.java index a139a01d1d..6319a7da92 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleInsertStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleInsertStatement.java @@ -3,8 +3,8 @@ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; import java.util.ArrayList; import java.util.List; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleErrorLoggingClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleReturningClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; @@ -16,13 +16,13 @@ public class OracleInsertStatement extends SQLInsertStatement implements OracleS private OracleReturningClause returning; private OracleErrorLoggingClause errorLogging; - private List hints = new ArrayList(); + private List hints = new ArrayList(); - public List getHints() { + public List getHints() { return hints; } - public void setHints(List hints) { + public void setHints(List hints) { this.hints = hints; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMergeStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMergeStatement.java index fac25896f2..4a63eb1733 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMergeStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMergeStatement.java @@ -4,10 +4,10 @@ import java.util.ArrayList; import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleErrorLoggingClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; @@ -16,7 +16,7 @@ public class OracleMergeStatement extends OracleStatementImpl { private static final long serialVersionUID = 1L; - private final List hints = new ArrayList(); + private final List hints = new ArrayList(); private SQLName into; private String alias; @@ -94,7 +94,7 @@ public class OracleMergeStatement extends OracleStatementImpl { this.errorLoggingClause = errorLoggingClause; } - public List getHints() { + public List getHints() { return hints; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java index d64b927229..e27db335ea 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java @@ -4,9 +4,9 @@ import java.util.ArrayList; import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.statement.SQLInsertInto; import com.alibaba.druid.sql.ast.statement.SQLSelect; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObject; import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleErrorLoggingClause; @@ -25,13 +25,13 @@ public class OracleMultiInsertStatement extends OracleStatementImpl { private SQLSelect subQuery; private Option option; private List entries = new ArrayList(); - private List hints = new ArrayList(); + private List hints = new ArrayList(1); - public List getHints() { + public List getHints() { return hints; } - public void setHints(List hints) { + public void setHints(List hints) { this.hints = hints; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectJoin.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectJoin.java index 1dbe718d5c..b9bb88557b 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectJoin.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectJoin.java @@ -20,7 +20,6 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -32,7 +31,6 @@ public class OracleSelectJoin extends SQLJoinTableSource implements OracleSelect private final List using = new ArrayList(); protected OracleSelectPivotBase pivot; protected FlashbackQueryClause flashback; - private List hints = new ArrayList(); public OracleSelectJoin(String alias){ super(alias); @@ -42,14 +40,6 @@ public class OracleSelectJoin extends SQLJoinTableSource implements OracleSelect } - public List getHints() { - return hints; - } - - public void setHints(List hints) { - this.hints = hints; - } - public FlashbackQueryClause getFlashback() { return flashback; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java index 00f64d52fc..3c24b2e9b6 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java @@ -18,10 +18,10 @@ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; import java.util.ArrayList; import java.util.List; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.statement.SQLSelectItem; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -30,7 +30,7 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock { private static final long serialVersionUID = 1L; - private final List hints = new ArrayList(1); + private final List hints = new ArrayList(1); private OracleSelectHierachicalQueryClause hierachicalQueryClause; private ModelClause modelClause; @@ -55,7 +55,7 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock { this.hierachicalQueryClause = hierachicalQueryClause; } - public List getHints() { + public List getHints() { return this.hints; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectSubqueryTableSource.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectSubqueryTableSource.java index f46b756d41..50a30f8efb 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectSubqueryTableSource.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectSubqueryTableSource.java @@ -15,12 +15,8 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; -import java.util.ArrayList; -import java.util.List; - import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -33,18 +29,7 @@ public class OracleSelectSubqueryTableSource extends SQLSubqueryTableSource impl protected FlashbackQueryClause flashback; - private List hints = new ArrayList(); - public OracleSelectSubqueryTableSource(){ - - } - - public List getHints() { - return hints; - } - - public void setHints(List hints) { - this.hints = hints; } public FlashbackQueryClause getFlashback() { @@ -82,7 +67,7 @@ public class OracleSelectSubqueryTableSource extends SQLSubqueryTableSource impl protected void accept0(OracleASTVisitor visitor) { if (visitor.visit(this)) { - acceptChild(visitor, this.hints); + acceptChild(visitor, this.getHints()); acceptChild(visitor, this.select); acceptChild(visitor, this.pivot); acceptChild(visitor, this.flashback); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java index aba1e3b901..413b702aba 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java @@ -15,11 +15,7 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; -import java.util.ArrayList; -import java.util.List; - import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; @@ -37,20 +33,11 @@ public class OracleSelectTableReference extends SQLExprTableSource implements Or protected SampleClause sampleClause; protected FlashbackQueryClause flashback; - private List hints = new ArrayList(); public OracleSelectTableReference(){ } - public List getHints() { - return hints; - } - - public void setHints(List hints) { - this.hints = hints; - } - public FlashbackQueryClause getFlashback() { return flashback; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableSource.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableSource.java index bcfdae958d..8f1de891ab 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableSource.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableSource.java @@ -15,10 +15,7 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; -import java.util.List; - import com.alibaba.druid.sql.ast.statement.SQLTableSource; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause; public interface OracleSelectTableSource extends SQLTableSource { @@ -30,6 +27,4 @@ public interface OracleSelectTableSource extends SQLTableSource { FlashbackQueryClause getFlashback(); void setFlashback(FlashbackQueryClause flashback); - - List getHints(); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleUpdateStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleUpdateStatement.java index f4d8f769d8..5a6cd98f99 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleUpdateStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleUpdateStatement.java @@ -19,22 +19,22 @@ import java.util.ArrayList; import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class OracleUpdateStatement extends SQLUpdateStatement implements OracleStatement { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private final List hints = new ArrayList(1); - private boolean only = false; - private String alias; - private SQLExpr where; + private final List hints = new ArrayList(1); + private boolean only = false; + private String alias; + private SQLExpr where; - private List returning = new ArrayList(); - private List returningInto = new ArrayList(); + private List returning = new ArrayList(); + private List returningInto = new ArrayList(); public OracleUpdateStatement(){ @@ -103,7 +103,7 @@ public class OracleUpdateStatement extends SQLUpdateStatement implements OracleS this.only = only; } - public List getHints() { + public List getHints() { return this.hints; } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java index 1e20f9a1d8..b251b960ca 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java @@ -21,6 +21,7 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLDataType; import com.alibaba.druid.sql.ast.SQLDataTypeImpl; import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; @@ -747,7 +748,7 @@ public class OracleExprParser extends SQLExprParser { return super.relationalRest(expr); } - public void parseHints(List hints) { + public void parseHints(List hints) { if (lexer.token() == Token.HINT) { hints.add(new OracleHint(lexer.stringVal())); lexer.nextToken(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java index 3c3cdc8fc7..16aa8810fd 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java @@ -286,6 +286,9 @@ public class OracleSelectParser extends SQLSelectParser { if (lexer.token() == (Token.ALL)) { union.setOperator(SQLUnionOperator.UNION_ALL); lexer.nextToken(); + } else if (lexer.token() == (Token.DISTINCT)) { + union.setOperator(SQLUnionOperator.DISTINCT); + lexer.nextToken(); } SQLSelectQuery right = query(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java index 8fcf028c39..ddcf4ec948 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java @@ -20,6 +20,7 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLDataTypeImpl; import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; @@ -39,7 +40,6 @@ import com.alibaba.druid.sql.ast.statement.SQLSetStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleErrorLoggingClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleParameter; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleReturningClause; @@ -115,7 +115,7 @@ public class OracleStatementParser extends SQLStatementParser { return (OracleExprParser) exprParser; } - public void parseHints(List hints) { + public void parseHints(List hints) { this.getExprParser().parseHints(hints); } @@ -128,7 +128,7 @@ public class OracleStatementParser extends SQLStatementParser { OracleInsertStatement stmt = (OracleInsertStatement) insertStatement; this.getExprParser().parseHints(stmt.getHints()); } else { - List hints = new ArrayList(); + List hints = new ArrayList(1); this.getExprParser().parseHints(hints); } } @@ -1243,7 +1243,7 @@ public class OracleStatementParser extends SQLStatementParser { accept(Token.INSERT); - List hints = new ArrayList(); + List hints = new ArrayList(); parseHints(hints); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index 4aff9453ec..6dd31d2faa 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -19,6 +19,7 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLDataType; import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLHint; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLSetQuantifier; @@ -200,7 +201,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } } - private void printHints(List hints) { + private void printHints(List hints) { if (hints.size() > 0) { print("/*+ "); printAndAccept(hints, ", "); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java index 3a65428301..22dd626c18 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -52,11 +52,11 @@ public class SQLSelectParser extends SQLParser { return select; } - + protected SQLUnionQuery createSQLUnionQuery() { return new SQLUnionQuery(); } - + public SQLUnionQuery unionRest(SQLUnionQuery union) { if (lexer.token() == Token.ORDER) { SQLOrderBy orderBy = this.createExprParser().parseOrderBy(); @@ -76,8 +76,10 @@ public class SQLSelectParser extends SQLParser { if (lexer.token() == Token.ALL) { union.setOperator(SQLUnionOperator.UNION_ALL); lexer.nextToken(); + } else if (lexer.token() == Token.DISTINCT) { + union.setOperator(SQLUnionOperator.DISTINCT); + lexer.nextToken(); } - SQLSelectQuery right = this.query(); union.setRight(right); @@ -240,18 +242,23 @@ public class SQLSelectParser extends SQLParser { } private void parseTableSourceQueryTableExpr(SQLExprTableSource tableReference) throws ParserException { - if (lexer.token() == Token.LITERAL_ALIAS || lexer.token() == Token.IDENTIFIED || lexer.token() == Token.LITERAL_CHARS) { + if (lexer.token() == Token.LITERAL_ALIAS || lexer.token() == Token.IDENTIFIED + || lexer.token() == Token.LITERAL_CHARS) { tableReference.setExpr(this.createExprParser().name()); return; } - + tableReference.setExpr(expr()); } protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) throws ParserException { if ((tableSource.getAlias() == null) || (tableSource.getAlias().length() == 0)) { if (lexer.token() != Token.LEFT && lexer.token() != Token.RIGHT && lexer.token() != Token.FULL) { - tableSource.setAlias(as()); + String alias = as(); + if (alias != null) { + tableSource.setAlias(alias); + return parseTableSourceRest(tableSource); + } } } @@ -289,6 +296,9 @@ public class SQLSelectParser extends SQLParser { } else if (lexer.token() == Token.COMMA) { lexer.nextToken(); joinType = SQLJoinTableSource.JoinType.COMMA; + } else if (identifierEquals("STRAIGHT_JOIN")) { + lexer.nextToken(); + joinType = SQLJoinTableSource.JoinType.STRAIGHT_JOIN; } if (joinType != null) { diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index af7e2c7440..73800481ef 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -797,7 +797,26 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { println(); print(x.getOperator().name); println(); - x.getRight().accept(this); + + boolean needParen = false; + + if (x.getOrderBy() != null) { + needParen = true; + } + + if (needParen) { + print('('); + x.getRight().accept(this); + print(')'); + } else { + x.getRight().accept(this); + } + + if (x.getOrderBy() != null) { + println(); + x.getOrderBy().accept(this); + } + return false; } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/cobar/DMLSelectParserTest.java b/src/test/java/com/alibaba/druid/bvt/sql/cobar/DMLSelectParserTest.java new file mode 100644 index 0000000000..b4ce8718ee --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/cobar/DMLSelectParserTest.java @@ -0,0 +1,83 @@ +package com.alibaba.druid.bvt.sql.cobar; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.parser.Token; + +public class DMLSelectParserTest extends TestCase { + + public void test_union_0() throws Exception { + String sql = "(select id from t1) union all (select id from t2) union all (select id from t3) ordeR By d desC limit 1 offset ?"; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT id\n" + // + "FROM t1\n" + // + "UNION ALL\n" + // + "SELECT id\n" + // + "FROM t2\n" + // + "UNION ALL\n" + // + "(SELECT id\n" + // + "FROM t3)\n" + // + "ORDER BY d DESC\n" + // + "LIMIT ?, 1", output); + } + + public void test_union_1() throws Exception { + String sql = "(select id from t1) union select id from t2 order by id union aLl (select id from t3) ordeR By d asC"; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT id\n" + // + "FROM t1\n" + // + "UNION\n" + // + "(SELECT id\n" + // + "FROM t2\n" + // + "ORDER BY id)\n" + // + "UNION ALL\n" + // + "(SELECT id\n" + // + "FROM t3)\n" + // + "ORDER BY d ASC", output); + } + + public void test_union_2() throws Exception { + String sql = "(select id from t1) union distInct (select id from t2) union select id from t3"; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT id\nFROM t1\nUNION DISTINCT\nSELECT id\nFROM t2\nUNION\nSELECT id\nFROM t3", output); + } + + public void test_select_0() throws Exception { + String sql = "SELect t1.id , t2.* from t1, test.t2 where test.t1.id=1 and t1.id=test.t2.id"; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT t1.id, t2.*\n" + // + "FROM t1, test.t2\n" + // + "WHERE test.t1.id = 1\n" + // + "\tAND t1.id = test.t2.id", output); + } + + public void test_select_1() throws Exception { + String sql = "select * from offer a straight_join wp_image b use key for join(t1,t2) on a.member_id=b.member_id inner join product_visit c where a.member_id=c.member_id and c.member_id='abc' "; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT *\n" + + // + "FROM offer a STRAIGHT_JOIN wp_image b USE INDEX FOR JOIN (t1, t2) ON a.member_id = b.member_id INNER JOIN product_visit c\n" + + // + "WHERE a.member_id = c.member_id\n" + // + "\tAND c.member_id = 'abc'", output); + } +}