From 9707e2fb23361b0719fdb612e5d3d1675f1b112f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A9=E5=B0=91?= Date: Wed, 14 Mar 2012 23:30:55 +0800 Subject: [PATCH] add sql wall support --- .../filter/wall/spi/MySqlWallVisitor.java | 26 +++++- .../filter/wall/spi/WallVisitorUtils.java | 89 +++++++++++++++++-- .../java/com/alibaba/druid/sql/SQLUtils.java | 59 ++++++++++-- .../druid/sql/parser/SQLExprParser.java | 2 +- .../bvt/filter/wall/WallVisitorUtilsTest.java | 24 +++++ 5 files changed, 181 insertions(+), 19 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/filter/wall/WallVisitorUtilsTest.java diff --git a/src/main/java/com/alibaba/druid/filter/wall/spi/MySqlWallVisitor.java b/src/main/java/com/alibaba/druid/filter/wall/spi/MySqlWallVisitor.java index eb6acaa223..e1b9663e55 100644 --- a/src/main/java/com/alibaba/druid/filter/wall/spi/MySqlWallVisitor.java +++ b/src/main/java/com/alibaba/druid/filter/wall/spi/MySqlWallVisitor.java @@ -4,10 +4,14 @@ import java.util.ArrayList; import java.util.List; import com.alibaba.druid.filter.wall.Violation; +import com.alibaba.druid.filter.wall.WallVisitor; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter; -public class MySqlWallVisitor extends MySqlASTVisitorAdapter { +public class MySqlWallVisitor extends MySqlASTVisitorAdapter implements WallVisitor { private final List violations; @@ -26,4 +30,24 @@ public class MySqlWallVisitor extends MySqlASTVisitorAdapter { public boolean visit(MySqlExecuteStatement x) { return false; } + + public boolean visit(SQLBinaryOpExpr x) { + WallVisitorUtils.check(this, x); + + return true; + } + + @Override + public boolean visit(SQLSelectQueryBlock x) { + if (x.getWhere() != null) { + x.getWhere().setParent(x); + } + + return true; + } + + @Override + public boolean visit(MySqlSelectQueryBlock x) { + return visit((SQLSelectQueryBlock) x); + } } diff --git a/src/main/java/com/alibaba/druid/filter/wall/spi/WallVisitorUtils.java b/src/main/java/com/alibaba/druid/filter/wall/spi/WallVisitorUtils.java index ec71e13054..427ca7db98 100644 --- a/src/main/java/com/alibaba/druid/filter/wall/spi/WallVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/filter/wall/spi/WallVisitorUtils.java @@ -3,29 +3,100 @@ package com.alibaba.druid.filter.wall.spi; import com.alibaba.druid.filter.wall.IllegalConditionViolation; import com.alibaba.druid.filter.wall.WallVisitor; import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; +import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; +import com.alibaba.druid.sql.ast.expr.SQLNotExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; public class WallVisitorUtils { public static void check(WallVisitor visitor, SQLBinaryOpExpr x) { + if (Boolean.TRUE == getObject(x)) { + visitor.getViolations().add(new IllegalConditionViolation(SQLUtils.toSQLString(x))); + } + } + + public static Object getObject(SQLBinaryOpExpr x) { if (x.getLeft() instanceof SQLLiteralExpr && x.getRight() instanceof SQLLiteralExpr) { if (x.getOperator() == SQLBinaryOperator.Equality) { String leftLiteral = SQLUtils.toOracleString(x.getLeft()); String rightLiteral = SQLUtils.toOracleString(x.getRight()); - if (leftLiteral.equalsIgnoreCase(rightLiteral)) { - visitor.getViolations().add(new IllegalConditionViolation(SQLUtils.toOracleString(x))); - } - } else if (x.getOperator() == SQLBinaryOperator.NotEqual) { - String leftLiteral = SQLUtils.toOracleString(x.getLeft()); - String rightLiteral = SQLUtils.toOracleString(x.getRight()); + return leftLiteral.equalsIgnoreCase(rightLiteral); + } - if (!leftLiteral.equalsIgnoreCase(rightLiteral)) { - visitor.getViolations().add(new IllegalConditionViolation(SQLUtils.toOracleString(x))); - } + if (x.getOperator() == SQLBinaryOperator.NotEqual || x.getOperator() == SQLBinaryOperator.LessThanOrGreater) { + String leftLiteral = SQLUtils.toSQLString(x.getLeft()); + String rightLiteral = SQLUtils.toSQLString(x.getRight()); + + return !leftLiteral.equalsIgnoreCase(rightLiteral); } } + + if (x.getOperator() == SQLBinaryOperator.Like) { + if (x.getRight() instanceof SQLCharExpr) { + String text = ((SQLCharExpr) x.getRight()).getText(); + + if (text.length() >= 0) { + for (char ch : text.toCharArray()) { + if (ch != '%') { + return null; + } + } + + return true; + } + + } + } + + x.getLeft().setParent(x); + x.getRight().setParent(x); + + if (x.getOperator() == SQLBinaryOperator.BooleanOr) { + Object leftResult = getValue(x.getLeft()); + Object rightResult = getValue(x.getRight()); + + if (Boolean.TRUE == leftResult || Boolean.TRUE == rightResult) { + return true; + } + } + + if (x.getOperator() == SQLBinaryOperator.BooleanAnd) { + Object leftResult = getValue(x.getLeft()); + Object rightResult = getValue(x.getRight()); + + if (Boolean.FALSE == leftResult || Boolean.FALSE == rightResult) { + return false; + } + + if (Boolean.TRUE == leftResult && Boolean.TRUE == rightResult) { + return true; + } + } + + return null; + } + + public static Object getValue(SQLExpr x) { + if (x instanceof SQLBinaryOpExpr) { + return getObject((SQLBinaryOpExpr) x); + } + + if (x instanceof MySqlBooleanExpr) { + return ((MySqlBooleanExpr) x).getValue(); + } + + if (x instanceof SQLNotExpr) { + Object result = getValue(((SQLNotExpr) x).getExpr()); + if (result != null && result instanceof Boolean) { + return !((Boolean) result).booleanValue(); + } + } + + return null; } } diff --git a/src/main/java/com/alibaba/druid/sql/SQLUtils.java b/src/main/java/com/alibaba/druid/sql/SQLUtils.java index a6d26d6619..877712312b 100644 --- a/src/main/java/com/alibaba/druid/sql/SQLUtils.java +++ b/src/main/java/com/alibaba/druid/sql/SQLUtils.java @@ -2,17 +2,32 @@ package com.alibaba.druid.sql; import java.util.List; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLStatement; +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; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; import com.alibaba.druid.sql.dialect.postgresql.parser.PGSQLStatementParser; import com.alibaba.druid.sql.dialect.postgresql.visitor.PGOutputVisitor; +import com.alibaba.druid.sql.parser.ParserException; +import com.alibaba.druid.sql.parser.SQLExprParser; +import com.alibaba.druid.sql.parser.Token; +import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; public class SQLUtils { + public static String toSQLString(SQLObject sqlObject) { + StringBuilder out = new StringBuilder(); + sqlObject.accept(new SQLASTOutputVisitor(out)); + + String sql = out.toString(); + return sql; + } + public static String toMySqlString(SQLObject sqlObject) { StringBuilder out = new StringBuilder(); sqlObject.accept(new MySqlOutputVisitor(out)); @@ -20,14 +35,28 @@ public class SQLUtils { String sql = out.toString(); return sql; } - + + public static SQLExpr toMySqlExpr(String sql) { + MySqlLexer lexer = new MySqlLexer(sql); + lexer.nextToken(); + + MySqlExprParser parser = new MySqlExprParser(lexer); + SQLExpr expr = parser.expr(); + + if (lexer.token() != Token.EOF) { + throw new ParserException("illegal sql expr : " + sql); + } + + return expr; + } + public static String formatMySql(String sql) { MySqlStatementParser parser = new MySqlStatementParser(sql); List statementList = parser.parseStatementList(); - + StringBuilder out = new StringBuilder(); MySqlOutputVisitor visitor = new MySqlOutputVisitor(out); - + for (SQLStatement stmt : statementList) { stmt.accept(visitor); } @@ -40,15 +69,15 @@ public class SQLUtils { List statementList = parser.parseStatementList(); StringBuilder out = new StringBuilder(); - + OracleOutputVisitor visitor; - + if (statementList.size() == 1) { visitor = new OracleOutputVisitor(out, false); } else { visitor = new OracleOutputVisitor(out, true); } - + for (SQLStatement stmt : statementList) { stmt.accept(visitor); } @@ -75,14 +104,28 @@ public class SQLUtils { public static String formatPGSql(String sql) { PGSQLStatementParser parser = new PGSQLStatementParser(sql); List statementList = parser.parseStatementList(); - + StringBuilder out = new StringBuilder(); PGOutputVisitor visitor = new PGOutputVisitor(out); - + for (SQLStatement stmt : statementList) { stmt.accept(visitor); } return out.toString(); } + + public static SQLExpr toSQLExpr(String sql) { + MySqlLexer lexer = new MySqlLexer(sql); + lexer.nextToken(); + + SQLExprParser parser = new SQLExprParser(lexer); + SQLExpr expr = parser.expr(); + + if (lexer.token() != Token.EOF) { + throw new ParserException("illegal sql expr : " + sql); + } + + return expr; + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java index 7f67fa595b..54dda9481f 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -275,7 +275,7 @@ public class SQLExprParser extends SQLParser { accept(Token.RPAREN); return primaryRest(sqlExpr); } else { - SQLExpr restExpr = primary(); + SQLExpr restExpr = expr(); sqlExpr = new SQLNotExpr(restExpr); } break; diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/WallVisitorUtilsTest.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/WallVisitorUtilsTest.java new file mode 100644 index 0000000000..83007269b2 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/WallVisitorUtilsTest.java @@ -0,0 +1,24 @@ +package com.alibaba.druid.bvt.filter.wall; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.filter.wall.spi.WallVisitorUtils; +import com.alibaba.druid.sql.SQLUtils; + +public class WallVisitorUtilsTest extends TestCase { + + public void test_isTrue() throws Exception { + Assert.assertEquals(Boolean.TRUE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("1 != 2"))); + Assert.assertEquals(Boolean.TRUE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("1 != 2 AND 2 = 2"))); + Assert.assertEquals(Boolean.FALSE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("1 != 2 AND 2 != 2"))); + Assert.assertEquals(Boolean.TRUE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("23 = 23"))); + Assert.assertEquals(Boolean.TRUE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("NOT 23 != 23"))); + Assert.assertEquals(Boolean.TRUE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("f1 like '%'"))); + Assert.assertEquals(Boolean.TRUE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("f1 like '%%'"))); + Assert.assertEquals(null, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("a1 = b1 AND f1 like '%%'"))); + Assert.assertEquals(Boolean.TRUE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("a1 = b1 OR f1 like '%%'"))); + + Assert.assertEquals(Boolean.TRUE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("1 < 2"))); + } +}