diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateTriggerStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateTriggerStatement.java index 0274d1de76..084403604f 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateTriggerStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateTriggerStatement.java @@ -18,6 +18,7 @@ package com.alibaba.druid.sql.ast.statement; import java.util.ArrayList; import java.util.List; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.SQLStatementImpl; @@ -27,15 +28,21 @@ public class SQLCreateTriggerStatement extends SQLStatementImpl implements SQLCr private SQLName name; - private boolean orReplace = false; + private boolean orReplace = false; private TriggerType triggerType; - private final List triggerEvents = new ArrayList(); + + private boolean update; + private boolean delete; + private boolean insert; private SQLName on; - private boolean forEachRow = false; + private boolean forEachRow = false; + private List updateOfColumns = new ArrayList(); + + private SQLExpr when; private SQLStatement body; public SQLCreateTriggerStatement() { @@ -49,7 +56,9 @@ public class SQLCreateTriggerStatement extends SQLStatementImpl implements SQLCr protected void accept0(SQLASTVisitor visitor) { if (visitor.visit(this)) { acceptChild(visitor, name); + acceptChild(visitor, updateOfColumns); acceptChild(visitor, on); + acceptChild(visitor, when); acceptChild(visitor, body); } visitor.endVisit(this); @@ -102,7 +111,7 @@ public class SQLCreateTriggerStatement extends SQLStatementImpl implements SQLCr } public List getTriggerEvents() { - return triggerEvents; + return null; } public boolean isForEachRow() { @@ -113,6 +122,45 @@ public class SQLCreateTriggerStatement extends SQLStatementImpl implements SQLCr this.forEachRow = forEachRow; } + public List getUpdateOfColumns() { + return updateOfColumns; + } + + public SQLExpr getWhen() { + return when; + } + + public void setWhen(SQLExpr when) { + if (when != null) { + when.setParent(this); + } + this.when = when; + } + + public boolean isUpdate() { + return update; + } + + public void setUpdate(boolean update) { + this.update = update; + } + + public boolean isDelete() { + return delete; + } + + public void setDelete(boolean delete) { + this.delete = delete; + } + + public boolean isInsert() { + return insert; + } + + public void setInsert(boolean insert) { + this.insert = insert; + } + public static enum TriggerType { BEFORE, AFTER, INSTEAD_OF } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java index 22250a0546..803a2529b8 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java @@ -50,6 +50,7 @@ public class MySqlLexer extends Lexer { map.put("BEGIN", Token.BEGIN); map.put("END", Token.END); map.put("DIV", Token.DIV); + map.put("MERGE", Token.MERGE); // for oceanbase & mysql 5.7 map.put("PARTITION", Token.PARTITION); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLCreateTableParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLCreateTableParser.java index e1a1b138f0..779f4c60b1 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLCreateTableParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLCreateTableParser.java @@ -18,11 +18,7 @@ package com.alibaba.druid.sql.parser; import java.util.List; import com.alibaba.druid.sql.ast.SQLName; -import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; -import com.alibaba.druid.sql.ast.statement.SQLConstraint; -import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; -import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; -import com.alibaba.druid.sql.ast.statement.SQLTableElement; +import com.alibaba.druid.sql.ast.statement.*; import com.alibaba.druid.util.JdbcConstants; public class SQLCreateTableParser extends SQLDDLParser { @@ -143,6 +139,12 @@ public class SQLCreateTableParser extends SQLDDLParser { } } + if (lexer.token == Token.AS) { + lexer.nextToken(); + SQLSelect select = this.createSQLSelectParser().select(); + createTable.setSelect(select); + } + return createTable; } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java index 0fcebd7f05..f888a20cbd 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java @@ -18,6 +18,7 @@ package com.alibaba.druid.sql.parser; import com.alibaba.druid.sql.dialect.db2.parser.DB2ExprParser; import com.alibaba.druid.sql.dialect.db2.parser.DB2Lexer; import com.alibaba.druid.sql.dialect.db2.parser.DB2StatementParser; +import com.alibaba.druid.sql.dialect.h2.parser.H2StatementParser; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlExprParser; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlLexer; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; 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 fa4fb748d4..c2a8485ea6 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -15,7 +15,6 @@ */ package com.alibaba.druid.sql.parser; -import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @@ -27,11 +26,9 @@ import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.ast.statement.*; import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement.TriggerEvent; import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement.TriggerType; -import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlRepeatStatement; import com.alibaba.druid.sql.dialect.oracle.parser.OracleExprParser; -import com.alibaba.druid.sql.dialect.postgresql.ast.stmt.*; +import com.alibaba.druid.util.FnvHash; import com.alibaba.druid.util.JdbcConstants; -import com.alibaba.druid.util.JdbcUtils; public class SQLStatementParser extends SQLParser { @@ -2012,13 +2009,13 @@ public class SQLStatementParser extends SQLParser { stmt.setName(this.exprParser.name()); - if (lexer.identifierEquals("BEFORE")) { + if (lexer.identifierEquals(FnvHash.Constants.BEFORE)) { stmt.setTriggerType(TriggerType.BEFORE); lexer.nextToken(); - } else if (lexer.identifierEquals("AFTER")) { + } else if (lexer.identifierEquals(FnvHash.Constants.AFTER)) { stmt.setTriggerType(TriggerType.AFTER); lexer.nextToken(); - } else if (lexer.identifierEquals("INSTEAD")) { + } else if (lexer.identifierEquals(FnvHash.Constants.INSTEAD)) { lexer.nextToken(); accept(Token.OF); stmt.setTriggerType(TriggerType.INSTEAD_OF); @@ -2027,21 +2024,26 @@ public class SQLStatementParser extends SQLParser { for (;;) { if (lexer.token == Token.INSERT) { lexer.nextToken(); - stmt.getTriggerEvents().add(TriggerEvent.INSERT); + stmt.setInsert(true); + } else if (lexer.token == Token.UPDATE) { + lexer.nextToken(); + stmt.setUpdate(true); + + if (lexer.token == Token.OF) { + lexer.nextToken(); + this.exprParser.names(stmt.getUpdateOfColumns(), stmt); + } + } else if (lexer.token == Token.DELETE) { + lexer.nextToken(); + stmt.setDelete(true); + } + + if (lexer.token == Token.COMMA + || lexer.token == Token.OR) { + lexer.nextToken(); continue; } - if (lexer.token == Token.UPDATE) { - lexer.nextToken(); - stmt.getTriggerEvents().add(TriggerEvent.UPDATE); - continue; - } - - if (lexer.token == Token.DELETE) { - lexer.nextToken(); - stmt.getTriggerEvents().add(TriggerEvent.DELETE); - continue; - } break; } @@ -2055,6 +2057,12 @@ public class SQLStatementParser extends SQLParser { stmt.setForEachRow(true); } + if (lexer.token == Token.WHEN) { + lexer.nextToken(); + SQLExpr condition = this.exprParser.expr(); + stmt.setWhen(condition); + } + List body = this.parseStatementList(); if (body == null || body.isEmpty()) { throw new ParserException("syntax error"); 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 2998c89453..0c22bc9def 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -2392,6 +2392,15 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet x.getInherits().accept(this); print(')'); } + + SQLSelect select = x.getSelect(); + if (select != null) { + println(); + print0(ucase ? "AS" : "as"); + + println(); + visit(select); + } } public boolean visit(SQLUniqueConstraint x) { @@ -3735,10 +3744,30 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet print0(ucase ? triggerTypeName : triggerTypeName.toLowerCase()); } - for (TriggerEvent event : x.getTriggerEvents()) { - print(' '); - print0(event.name()); + if (x.isInsert()) { + print0(ucase ? " INSERT" : " insert"); } + + if (x.isDelete()) { + if (x.isInsert()) { + print0(ucase ? " OR" : " or"); + } + print0(ucase ? " DELETE" : " delete"); + } + + if (x.isUpdate()) { + if (x.isInsert() || x.isDelete()) { + print0(ucase ? " OR" : " or"); + } + print0(ucase ? " UPDATE" : " update"); + + List colums = x.getUpdateOfColumns(); + for (SQLName colum : colums) { + print(' '); + colum.accept(this); + } + } + println(); print0(ucase ? "ON " : "on "); x.getOn().accept(this); @@ -3747,6 +3776,13 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet println(); print0(ucase ? "FOR EACH ROW" : "for each row"); } + + SQLExpr when = x.getWhen(); + if (when != null) { + println(); + print0(ucase ? "WHEN " : "when "); + when.accept(this); + } this.indentCount--; println(); x.getBody().accept(this); diff --git a/src/main/java/com/alibaba/druid/util/FnvHash.java b/src/main/java/com/alibaba/druid/util/FnvHash.java index 4e25e52df2..4430db9f32 100644 --- a/src/main/java/com/alibaba/druid/util/FnvHash.java +++ b/src/main/java/com/alibaba/druid/util/FnvHash.java @@ -410,5 +410,9 @@ public final class FnvHash { long HOUR = fnv1a_64_lower("HOUR"); long MINUTE = fnv1a_64_lower("MINUTE"); long SECOND = fnv1a_64_lower("SECOND"); + + long BEFORE = fnv1a_64_lower("BEFORE"); + long AFTER = fnv1a_64_lower("AFTER"); + long INSTEAD = fnv1a_64_lower("INSTEAD"); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateTriggerTest2.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateTriggerTest2.java new file mode 100644 index 0000000000..41f45fa7fd --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateTriggerTest2.java @@ -0,0 +1,49 @@ +/* + * 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.bvt.sql.mysql.create; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import org.junit.Assert; + +import java.util.List; + +public class MySqlCreateTriggerTest2 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "create trigger employee_trigger after insert,update,delete on employee for each row call \"test.redisson.h2cluster.trigger.ClusterTrigger\""; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); +// print(statementList); + + Assert.assertEquals(1, statementList.size()); + } + + public void test_1() throws Exception { + String sql = "CREATE TRIGGER testref BEFORE INSERT ON test1" + " FOR EACH ROW" + " BEGIN" + + " INSERT INTO test2 SET a2 = NEW.a1;" + " DELETE FROM test3 WHERE a3 = NEW.a1;" + + " UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;" + " END;"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); +// print(statementList); + + Assert.assertEquals(1, statementList.size()); + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreateTriggerTest3.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreateTriggerTest3.java new file mode 100644 index 0000000000..6fd116b746 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreateTriggerTest3.java @@ -0,0 +1,72 @@ +/* + * 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.bvt.sql.oracle.create; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; +import org.junit.Assert; + +import java.util.List; + +public class OracleCreateTriggerTest3 extends OracleTest { + + public void test_0() throws Exception { + String sql = // + "CREATE TRIGGER salary_check\n" + + " BEFORE INSERT OR UPDATE OF salary, job_id ON employees\n" + + " FOR EACH ROW\n" + + " WHEN (new.job_id <> 'AD_VP')\n" + + " CALL check_sal(:new.job_id, :new.salary, :new.last_name)"; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + Assert.assertEquals("CREATE TRIGGER salary_check\n" + + "\tBEFORE INSERT OR UPDATE salary job_id\n" + + "\tON employees\n" + + "\tFOR EACH ROW\n" + + "\tWHEN new.job_id <> 'AD_VP'\n" + + "CALL check_sal(:new.job_id, :new.salary, :new.last_name)",// + SQLUtils.toSQLString(stmt, JdbcConstants.ORACLE)); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(0, visitor.getTables().size()); + + // Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("cdc.en_complaint_ipr_stat_fdt0"))); + + Assert.assertEquals(0, visitor.getColumns().size()); + + // Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("pivot_table", "*"))); + // Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("pivot_table", "YEAR"))); + // Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("pivot_table", "order_mode"))); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/postgresql/PGCreateTableTest_6.java b/src/test/java/com/alibaba/druid/bvt/sql/postgresql/PGCreateTableTest_6.java new file mode 100644 index 0000000000..bc61e38d79 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/postgresql/PGCreateTableTest_6.java @@ -0,0 +1,62 @@ +/* + * 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.bvt.sql.postgresql; + +import com.alibaba.druid.sql.PGTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.postgresql.parser.PGSQLStatementParser; +import com.alibaba.druid.sql.dialect.postgresql.visitor.PGSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import org.junit.Assert; + +import java.util.List; + +public class PGCreateTableTest_6 extends PGTest { + + public void test_0() throws Exception { + String sql = "create table xxx as select * from xxx"; + + PGSQLStatementParser parser = new PGSQLStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + Assert.assertEquals("CREATE TABLE xxx\n" + + "AS\n" + + "SELECT *\n" + + "FROM xxx", SQLUtils.toPGString(stmt)); + + Assert.assertEquals("create table xxx\n" + + "as\n" + + "select *\n" + + "from xxx", SQLUtils.toPGString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); + + Assert.assertEquals(1, statementList.size()); + + PGSchemaStatVisitor visitor = new PGSchemaStatVisitor(); + stmt.accept(visitor); + +// System.out.println("Tables : " + visitor.getTables()); +// System.out.println("fields : " + visitor.getColumns()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("xxx"))); + + Assert.assertTrue(visitor.getTables().get(new TableStat.Name("xxx")).getCreateCount() == 1); + + Assert.assertTrue(visitor.getColumns().size() == 1); + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/postgresql/select/PGSelectTest56.java b/src/test/java/com/alibaba/druid/bvt/sql/postgresql/select/PGSelectTest56.java index 4919c86dfe..2b5e063c0f 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/postgresql/select/PGSelectTest56.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/postgresql/select/PGSelectTest56.java @@ -39,21 +39,12 @@ public class PGSelectTest56 extends TestCase { "\tAND cs.CreateDate <= to_date('2017-08-31 23:00:00', 'YYYY-MM-DD HH24:MI:SS')\n" + "GROUP BY to_char(CreateDate || ' ' || CAST(HourArgment AS VARCHAR) || ':00:00'::TIMESTAMP, 'YYYY-MM-DD HH24')", SQLUtils.toPGString(stmt)); - assertEquals("select b.*, st_astext(p.pos) as pos, st_astext(p.polygon) as polygon\n" + - "from ts_biz b\n" + - "\tjoin (\n" + - "\t\tselect *\n" + - "\t\tfrom ts_polygon\n" + - "\t\twhere type in (?)\n" + - "\t\t\tand st_intersects(st_transform(ST_GeomFromText(?, 4326), 26986), st_transform(polygon, 26986))\n" + - "\t) p\n" + - "\ton b.objid = p.objid\n" + - "\t\tand b.type = p.type\n" + - "\t\tand (b.cp_code = p.cp_code\n" + - "\t\t\tor (b.cp_code is null\n" + - "\t\t\t\tand p.cp_code is null))\n" + - "where b.cp_code = ?\n" + - "\tor b.cp_code is null", SQLUtils.toPGString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); + assertEquals("select to_char(CreateDate || ' ' || cast(HourArgment as VARCHAR) || ':00:00'::TIMESTAMP, 'YYYY-MM-DD HH24') as \"intoTime \"\n" + + "from analyzedata.StatTime cs\n" + + "where 1 = 1\n" + + "\tand cs.CreateDate >= to_date('2017-08-31 00:00:00', 'YYYY-MM-DD HH24:MI:SS')\n" + + "\tand cs.CreateDate <= to_date('2017-08-31 23:00:00', 'YYYY-MM-DD HH24:MI:SS')\n" + + "group by to_char(CreateDate || ' ' || cast(HourArgment as VARCHAR) || ':00:00'::TIMESTAMP, 'YYYY-MM-DD HH24')", SQLUtils.toPGString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); assertEquals(1, stmtList.size()); @@ -64,7 +55,7 @@ public class PGSelectTest56 extends TestCase { // System.out.println("fields : " + visitor.getColumns()); // System.out.println("coditions : " + visitor.getConditions()); - assertEquals(7, visitor.getColumns().size()); - assertEquals(2, visitor.getTables().size()); + assertEquals(2, visitor.getColumns().size()); + assertEquals(1, visitor.getTables().size()); } }