diff --git a/src/main/java/com/alibaba/druid/mapping/spi/MappingVisitorUtils.java b/src/main/java/com/alibaba/druid/mapping/spi/MappingVisitorUtils.java index 5d5fff7f3a..bd193ed40f 100644 --- a/src/main/java/com/alibaba/druid/mapping/spi/MappingVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/mapping/spi/MappingVisitorUtils.java @@ -127,7 +127,7 @@ public class MappingVisitorUtils { } public static void setDataSource(MappingEngine engine, SQLDeleteStatement stmt) { - if (stmt.getTableSource() == null) { + if (stmt.getExprTableSource() == null) { Entity entity = engine.getFirstEntity(); stmt.setTableSource(new SQLIdentifierExpr(entity.getName())); } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLDeleteStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLDeleteStatement.java index de3827ee51..57aaa51b26 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLDeleteStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLDeleteStatement.java @@ -23,34 +23,38 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class SQLDeleteStatement extends SQLStatementImpl { - private static final long serialVersionUID = 1L; - protected SQLExprTableSource tableSource; - protected SQLExpr where; + private static final long serialVersionUID = 1L; + protected SQLTableSource tableSource; + protected SQLExpr where; public SQLDeleteStatement(){ } - public SQLExprTableSource getTableSource() { + public SQLTableSource getTableSource() { return tableSource; } - + + public SQLExprTableSource getExprTableSource() { + return (SQLExprTableSource) getTableSource(); + } + public void setTableSource(SQLExpr expr) { this.setTableSource(new SQLExprTableSource(expr)); } - public void setTableSource(SQLExprTableSource tableSource) { + public void setTableSource(SQLTableSource tableSource) { this.tableSource = tableSource; } public SQLName getTableName() { - return (SQLName) tableSource.getExpr(); + return (SQLName) getExprTableSource().getExpr(); } public void setTableName(SQLName tableName) { this.setTableSource(new SQLExprTableSource(tableName)); } - + public void setTableName(String name) { setTableName(new SQLIdentifierExpr(name)); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDeleteStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDeleteStatement.java index 8810b87d72..1fd77139f1 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDeleteStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDeleteStatement.java @@ -15,13 +15,8 @@ */ package com.alibaba.druid.sql.dialect.mysql.ast.statement; -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; -import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; @@ -30,17 +25,16 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class MySqlDeleteStatement extends SQLDeleteStatement { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private boolean lowPriority = false; - private boolean quick = false; - private boolean ignore = false; + private boolean lowPriority = false; + private boolean quick = false; + private boolean ignore = false; - private SQLTableSource from; - private SQLTableSource using; - private SQLOrderBy orderBy; - private Limit limit; - private final List tableSources = new ArrayList(); + private SQLTableSource from; + private SQLTableSource using; + private SQLOrderBy orderBy; + private Limit limit; public boolean isLowPriority() { return lowPriority; @@ -78,25 +72,6 @@ public class MySqlDeleteStatement extends SQLDeleteStatement { this.using = using; } - public SQLName getTableName() { - if (tableSources.size() == 0) { - return null; - } - return (SQLName) tableSources.get(0).getExpr(); - } - - public void setTableSource(SQLExprTableSource tableSource) { - if (tableSources.size() == 0) { - tableSources.add(tableSource); - } else { - tableSources.set(0, tableSource); - } - } - - public List getTableSources() { - return tableSources; - } - public void setFrom(SQLTableSource from) { this.from = from; } @@ -132,7 +107,7 @@ public class MySqlDeleteStatement extends SQLDeleteStatement { protected void accept0(MySqlASTVisitor visitor) { if (visitor.visit(this)) { - acceptChild(visitor, getTableSources()); + acceptChild(visitor, getTableSource()); acceptChild(visitor, getWhere()); acceptChild(visitor, getFrom()); acceptChild(visitor, getUsing()); 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 7385010344..8294791e0a 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 @@ -21,6 +21,7 @@ import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause; 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.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; @@ -262,4 +263,13 @@ public class MySqlSelectParser extends SQLSelectParser { protected MySqlExprParser createExprParser() { return new MySqlExprParser(lexer); } + + protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) throws ParserException { + if (identifierEquals("USING")) { + return tableSource; + } + + return super.parseTableSourceRest(tableSource); + } + } 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 faea699089..3741ba13a6 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 @@ -27,7 +27,6 @@ import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement; -import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; @@ -58,6 +57,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionSt import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLExprParser; +import com.alibaba.druid.sql.parser.SQLSelectParser; import com.alibaba.druid.sql.parser.SQLStatementParser; import com.alibaba.druid.sql.parser.Token; @@ -109,17 +109,7 @@ public class MySqlStatementParser extends SQLStatementParser { } if (lexer.token() == Token.IDENTIFIER) { - for (;;) { - SQLName name = exprParser.name(); - deleteStatement.getTableSources().add(new SQLExprTableSource(name)); - - if (lexer.token() == Token.COMMA) { - lexer.nextToken(); - continue; - } - - break; - } + deleteStatement.setTableSource(createSQLSelectParser().parseTableSource()); if (lexer.token() == Token.FROM) { lexer.nextToken(); @@ -129,17 +119,7 @@ public class MySqlStatementParser extends SQLStatementParser { } else { if (lexer.token() == Token.FROM) { lexer.nextToken(); - } - for (;;) { - SQLName name = exprParser.name(); - deleteStatement.getTableSources().add(new SQLExprTableSource(name)); - - if (lexer.token() == Token.COMMA) { - lexer.nextToken(); - continue; - } - - break; + deleteStatement.setTableSource(createSQLSelectParser().parseTableSource()); } } @@ -1039,4 +1019,8 @@ public class MySqlStatementParser extends SQLStatementParser { } return stmt; } + + public SQLSelectParser createSQLSelectParser() { + return new MySqlSelectParser(this.lexer); + } } 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 48e2e42a0a..8be785ac4f 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 @@ -623,9 +623,9 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV if (x.getFrom() == null) { print("FROM "); - printAndAccept(x.getTableSources(), ", "); + x.getTableSource().accept(this); } else { - printAndAccept(x.getTableSources(), ", "); + x.getTableSource().accept(this); println(); print("FROM "); x.getFrom().accept(this); 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 d557989f31..6fbf20ca81 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 @@ -2,7 +2,6 @@ package com.alibaba.druid.sql.dialect.mysql.visitor; import java.util.Map; -import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement; @@ -66,17 +65,20 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS setAliasMap(); - if (x.getTableSources().size() == 1) { - SQLName tableName = (SQLName) x.getTableSources().get(0).getExpr(); + if (x.getTableSource() instanceof SQLExprTableSource) { + SQLName tableName = (SQLName) ((SQLExprTableSource) x.getTableSource()).getExpr(); String ident = tableName.toString(); setCurrentTable(x, ident); - } - for (SQLExprTableSource tableSource : x.getTableSources()) { - SQLExpr tableName = tableSource.getExpr(); - String ident = tableName.toString(); - TableStat stat = getTableStat(ident); + TableStat stat = this.getTableStat(ident); stat.incrementDeleteCount(); + } else { + MySqlSchemaStatVisitor tableSourceVisitor = new MySqlSchemaStatVisitor(); + tableSourceVisitor.accept(x.getTableSource()); + for (TableStat.Name name : tableSourceVisitor.getTables().keySet()) { + TableStat stat = this.getTableStat(name.getName()); + stat.incrementDeleteCount(); + } } accept(x.getWhere()); @@ -483,6 +485,6 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS @Override public void endVisit(MySqlOutFileExpr x) { - + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGSchemaStatVisitor.java index 76932dd639..4801e0b203 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGSchemaStatVisitor.java @@ -232,7 +232,6 @@ public class PGSchemaStatVisitor extends SchemaStatVisitor implements PGASTVisit @Override public boolean visit(PGSelectQueryBlock x) { - this.visit((SQLSelectQueryBlock) x); - return false; + return this.visit((SQLSelectQueryBlock) x); } } 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 44f7986214..5982c4860e 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -235,7 +235,7 @@ public class SQLSelectParser extends SQLParser { tableReference.setExpr(expr()); } - private SQLTableSource parseTableSourceRest(SQLTableSource tableSource) throws ParserException { + 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()); diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java index 188604a9f9..b96f887999 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java @@ -472,9 +472,16 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { x.getFrom().accept(this); return false; } + + if (x.getInto() != null) { + SQLName into = (SQLName) x.getInto().getExpr(); + String ident = into.toString(); + TableStat stat = getTableStat(ident); + stat.incrementInsertCount(); + } String originalTable = getCurrentTable(); - + if (x.getFrom() instanceof SQLExprTableSource) { SQLExprTableSource tableSource = (SQLExprTableSource) x.getFrom(); if (tableSource.getExpr() instanceof SQLName) { @@ -488,7 +495,7 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { x.putAttribute("_old_local_", originalTable); } } - + if (x.getFrom() != null) { x.getFrom().accept(this); // 提前执行,获得aliasMap String table = (String) x.getFrom().getAttribute(ATTR_TABLE); diff --git a/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java b/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java index 33af158a56..8572d8f42c 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java +++ b/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java @@ -85,6 +85,8 @@ public class MySqlWallVisitor extends MySqlASTVisitorAdapter implements WallVisi @Override public boolean visit(MySqlDeleteStatement x) { + WallVisitorUtils.checkReadOnly(this, x.getFrom()); + return visit((SQLDeleteStatement) x); } diff --git a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java index a82e4f9de6..779fd69d68 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java @@ -112,7 +112,7 @@ public class WallVisitorUtils { } public static void checkDelete(WallVisitor visitor, SQLDeleteStatement x) { - checkReadOnly(visitor, x.getTableSource()); + checkReadOnly(visitor, x.getExprTableSource()); if (!visitor.getConfig().isDeleteAllow()) { addViolation(visitor, x); diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/WallReadOnlyTest.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/WallReadOnlyTest.java new file mode 100644 index 0000000000..9cb54c541c --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/WallReadOnlyTest.java @@ -0,0 +1,39 @@ +package com.alibaba.druid.bvt.filter.wall; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.wall.WallConfig; +import com.alibaba.druid.wall.WallUtils; + +/** + * 这个场景,检测可疑的Having条件 + * @author wenshao + * + */ +public class WallReadOnlyTest extends TestCase { + private WallConfig config = new WallConfig(); + + protected void setUp() throws Exception { + config.getReadOnlyTables().add("members"); + } + private String sql = "SELECT F1, F2 members"; + private String insert_sql = "INSERT INTO members (FID, FNAME) VALUES (?, ?)"; + private String update_sql = "UPDATE members SET FNAME = ? WHERe FID = ?"; + private String delete_sql = "DELETE members WHERE FID = ?"; + + public void testMySql() throws Exception { + Assert.assertTrue(WallUtils.isValidateMySql(sql, config)); + Assert.assertFalse(WallUtils.isValidateMySql(insert_sql, config)); + Assert.assertFalse(WallUtils.isValidateMySql(update_sql, config)); + Assert.assertFalse(WallUtils.isValidateMySql(delete_sql, config)); + } + + public void testORACLE() throws Exception { + Assert.assertTrue(WallUtils.isValidateOracle(sql, config)); + Assert.assertFalse(WallUtils.isValidateOracle(insert_sql, config)); + Assert.assertFalse(WallUtils.isValidateOracle(update_sql, config)); + Assert.assertFalse(WallUtils.isValidateOracle(delete_sql, config)); + } +}