From 1f3c5467eb134b5eb66841409a722d082c1953be Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 11 Feb 2017 19:20:39 +0800 Subject: [PATCH] improved mysql parser, support more create view syntax. --- .../ast/statement/SQLCreateViewStatement.java | 31 +++++ .../mysql/parser/MySqlStatementParser.java | 55 +-------- .../druid/sql/parser/SQLStatementParser.java | 24 ++++ .../sql/visitor/SQLASTOutputVisitor.java | 25 ++++ .../mysql/create/MySqlCreateViewTest1.java | 110 ++++++++++++++++++ 5 files changed, 194 insertions(+), 51 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateViewTest1.java diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateViewStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateViewStatement.java index 8baa8d8830..a4e081bc89 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateViewStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateViewStatement.java @@ -33,6 +33,10 @@ public class SQLCreateViewStatement extends SQLStatementImpl implements SQLDDLSt protected SQLSelect subQuery; protected boolean ifNotExists = false; + protected String algorithm; + protected SQLName definer; + protected String sqlSecurity; + protected final List columns = new ArrayList(); private Level with; @@ -109,6 +113,33 @@ public class SQLCreateViewStatement extends SQLStatementImpl implements SQLDDLSt this.comment = comment; } + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public SQLName getDefiner() { + return definer; + } + + public void setDefiner(SQLName definer) { + if (definer != null) { + definer.setParent(this); + } + this.definer = definer; + } + + public String getSqlSecurity() { + return sqlSecurity; + } + + public void setSqlSecurity(String sqlSecurity) { + this.sqlSecurity = sqlSecurity; + } + public void output(StringBuffer buf) { buf.append("CREATE VIEW "); this.name.output(buf); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java index 353945fcde..00b24a26c7 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java @@ -37,55 +37,8 @@ import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLNCharExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; -import com.alibaba.druid.sql.ast.statement.SQLAlterDatabaseStatement; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddColumn; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddConstraint; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddIndex; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddPartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableAnalyzePartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableCheckPartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableCoalescePartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableConvertCharSet; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDisableConstraint; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDisableKeys; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDiscardPartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropColumnItem; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropConstraint; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropForeignKey; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropIndex; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropKey; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropPartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropPrimaryKey; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableEnableConstraint; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableEnableKeys; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableImportPartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableOptimizePartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableReOrganizePartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableRebuildPartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableRepairPartition; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement; -import com.alibaba.druid.sql.ast.statement.SQLAlterTableTruncatePartition; -import com.alibaba.druid.sql.ast.statement.SQLBlockStatement; -import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; -import com.alibaba.druid.sql.ast.statement.SQLCreateDatabaseStatement; -import com.alibaba.druid.sql.ast.statement.SQLCreateIndexStatement; -import com.alibaba.druid.sql.ast.statement.SQLCreateProcedureStatement; -import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; -import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; -import com.alibaba.druid.sql.ast.statement.SQLIfStatement; -import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.ast.statement.*; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause; -import com.alibaba.druid.sql.ast.statement.SQLLoopStatement; -import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey; -import com.alibaba.druid.sql.ast.statement.SQLSelect; -import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; -import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; -import com.alibaba.druid.sql.ast.statement.SQLSetStatement; -import com.alibaba.druid.sql.ast.statement.SQLShowTablesStatement; -import com.alibaba.druid.sql.ast.statement.SQLTableSource; -import com.alibaba.druid.sql.ast.statement.SQLUnique; -import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; -import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue; import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue.ConditionType; @@ -179,7 +132,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTableStatusSta import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTriggersStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; -import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnlockTablesStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MysqlDeallocatePrepareStatement; @@ -376,7 +328,8 @@ public class MySqlStatementParser extends SQLStatementParser { return parseCreateUser(); } - if (lexer.token() == Token.VIEW) { + if (lexer.token() == Token.VIEW + || identifierEquals("ALGORITHM")) { if (replace) { lexer.reset(markBp, markChar, Token.CREATE); } @@ -398,7 +351,7 @@ public class MySqlStatementParser extends SQLStatementParser { return parseCreateProcedure(); } - throw new ParserException("TODO " + lexer.token()); + throw new ParserException("TODO " + lexer.info()); } public SQLStatement parseCreateIndex(boolean acceptCreate) { diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java index 159d893081..efce85b0ac 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -1927,6 +1927,30 @@ public class SQLStatementParser extends SQLParser { createView.setOrReplace(true); } + if (identifierEquals("ALGORITHM")) { + lexer.nextToken(); + accept(Token.EQ); + String algorithm = lexer.stringVal(); + createView.setAlgorithm(algorithm); + lexer.nextToken(); + } + + if (identifierEquals("DEFINER")) { + lexer.nextToken(); + accept(Token.EQ); + SQLName definer = this.exprParser.name(); + createView.setDefiner(definer); + lexer.nextToken(); + } + + if (identifierEquals("SQL")) { + lexer.nextToken(); + acceptIdentifier("SECURITY"); + String sqlSecurity = lexer.stringVal(); + createView.setSqlSecurity(sqlSecurity); + lexer.nextToken(); + } + this.accept(Token.VIEW); if (lexer.token() == Token.IF || identifierEquals("IF")) { 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 c9fd08787a..0cb2aeb78d 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -2053,6 +2053,31 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet if (x.isOrReplace()) { print0(ucase ? "OR REPLACE " : "or replace "); } + + incrementIndent(); + String algorithm = x.getAlgorithm(); + if (algorithm != null && algorithm.length() > 0) { + print0(ucase ? "ALGORITHM = " : "algorithm = "); + print0(algorithm); + println(); + } + + SQLName definer = x.getDefiner(); + if (definer != null) { + print0(ucase ? "DEFINER = " : "definer = "); + definer.accept(this); + println(); + } + + String sqlSecurity = x.getSqlSecurity(); + if (sqlSecurity != null && sqlSecurity.length() > 0) { + print0(ucase ? "SQL SECURITY = " : "sql security = "); + print0(sqlSecurity); + println(); + } + + decrementIndent(); + print0(ucase ? "VIEW " : "view "); if (x.isIfNotExists()) { diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateViewTest1.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateViewTest1.java new file mode 100644 index 0000000000..928b7d3071 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateViewTest1.java @@ -0,0 +1,110 @@ +/* + * Copyright 1999-2101 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.bvt.sql.mysql.create; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateViewStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import com.alibaba.druid.stat.TableStat.Column; +import org.junit.Assert; + +import java.util.List; + +public class MySqlCreateViewTest1 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "CREATE ALGORITHM=UNDEFINED DEFINER=root@localhost SQL SECURITY DEFINER VIEW view_audit_enroll AS " + + "SELECT a.enroll_id AS 'enrollId', case when ((SELECT audit FROM actvty_audit WHERE enroll_id = a.enroll_id AND rankjurisdiction = 1) > 0) then \"县站已审核\" else NULL end AS 'countyAudit', case when ((SELECT audit FROM actvty_audit WHERE enroll_id = a.enroll_id AND rankjurisdiction = 2) > 0) then \"市馆已审核\" else NULL end AS 'cityAudit', case when ((SELECT audit FROM actvty_audit WHERE enroll_id = a.enroll_id AND rankjurisdiction = 3) > 0) then \"省馆已审核\" else NULL end AS 'provinceAudit' FROM actvty_audit a GROUP BY a.enroll_id"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLCreateViewStatement stmt = (SQLCreateViewStatement) statementList.get(0); +// print(statementList); + + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + stmt.accept(visitor); + + Assert.assertEquals("CREATE ALGORITHM = UNDEFINED\n" + + "\tDEFINER = root\n" + + "\tSQL SECURITY = DEFINER\n" + + "\tVIEW view_audit_enroll\n" + + "AS\n" + + "SELECT a.enroll_id AS 'enrollId', CASE WHEN (\n" + + "\t\tSELECT audit\n" + + "\t\tFROM actvty_audit\n" + + "\t\tWHERE enroll_id = a.enroll_id\n" + + "\t\t\tAND rankjurisdiction = 1\n" + + "\t\t) > 0 THEN '县站已审核' ELSE NULL END AS 'countyAudit', CASE WHEN (\n" + + "\t\tSELECT audit\n" + + "\t\tFROM actvty_audit\n" + + "\t\tWHERE enroll_id = a.enroll_id\n" + + "\t\t\tAND rankjurisdiction = 2\n" + + "\t\t) > 0 THEN '市馆已审核' ELSE NULL END AS 'cityAudit', CASE WHEN (\n" + + "\t\tSELECT audit\n" + + "\t\tFROM actvty_audit\n" + + "\t\tWHERE enroll_id = a.enroll_id\n" + + "\t\t\tAND rankjurisdiction = 3\n" + + "\t\t) > 0 THEN '省馆已审核' ELSE NULL END AS 'provinceAudit'\n" + + "FROM actvty_audit a\n" + + "GROUP BY a.enroll_id", // + SQLUtils.toMySqlString(stmt)); + + Assert.assertEquals("create algorithm = UNDEFINED\n" + + "\tdefiner = root\n" + + "\tsql security = DEFINER\n" + + "\tview view_audit_enroll\n" + + "as\n" + + "select a.enroll_id as 'enrollId', case when (\n" + + "\t\tselect audit\n" + + "\t\tfrom actvty_audit\n" + + "\t\twhere enroll_id = a.enroll_id\n" + + "\t\t\tand rankjurisdiction = 1\n" + + "\t\t) > 0 then '县站已审核' else null end as 'countyAudit', case when (\n" + + "\t\tselect audit\n" + + "\t\tfrom actvty_audit\n" + + "\t\twhere enroll_id = a.enroll_id\n" + + "\t\t\tand rankjurisdiction = 2\n" + + "\t\t) > 0 then '市馆已审核' else null end as 'cityAudit', case when (\n" + + "\t\tselect audit\n" + + "\t\tfrom actvty_audit\n" + + "\t\twhere enroll_id = a.enroll_id\n" + + "\t\t\tand rankjurisdiction = 3\n" + + "\t\t) > 0 then '省馆已审核' else null end as 'provinceAudit'\n" + + "from actvty_audit a\n" + + "group by a.enroll_id", // + SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); + +// System.out.println("Tables : " + visitor.getTables()); +// System.out.println("fields : " + visitor.getColumns()); +// System.out.println("coditions : " + visitor.getConditions()); +// System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(3, visitor.getColumns().size()); + Assert.assertEquals(2, visitor.getConditions().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("actvty_audit"))); + + Assert.assertTrue(visitor.getColumns().contains(new Column("actvty_audit", "audit"))); + Assert.assertTrue(visitor.getColumns().contains(new Column("actvty_audit", "enroll_id"))); + } +}