From 0223e5d13c4babf166bcea6b4411eb3b01c22e9a Mon Sep 17 00:00:00 2001 From: wenshao Date: Mon, 18 Mar 2013 23:23:23 +0800 Subject: [PATCH] improve WallFilter --- .../visitor/SQLServerEvalVisitor.java | 119 ++++++++++++++++++ .../sql/visitor/SQLEvalVisitorUtils.java | 54 +++++++- .../com/alibaba/druid/wall/WallProvider.java | 16 ++- .../druid/bvt/filter/wall/CountTest.java | 39 ++++++ .../druid/bvt/filter/wall/LikeTest.java | 34 +++++ .../druid/bvt/filter/wall/MySqlWallTest2.java | 19 +-- 6 files changed, 256 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerEvalVisitor.java create mode 100644 src/test/java/com/alibaba/druid/bvt/filter/wall/CountTest.java create mode 100644 src/test/java/com/alibaba/druid/bvt/filter/wall/LikeTest.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerEvalVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerEvalVisitor.java new file mode 100644 index 0000000000..724ebc13a7 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerEvalVisitor.java @@ -0,0 +1,119 @@ +/* + * Copyright 1999-2011 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.sql.dialect.sqlserver.visitor; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; +import com.alibaba.druid.sql.ast.expr.SQLCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLInListExpr; +import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.expr.SQLNullExpr; +import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; +import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.visitor.SQLEvalVisitor; +import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; + +public class SQLServerEvalVisitor extends SQLServerASTVisitorAdapter implements SQLEvalVisitor { + + private List parameters = new ArrayList(); + + private int variantIndex = -1; + + private boolean markVariantIndex = true; + + public SQLServerEvalVisitor(){ + this(new ArrayList(1)); + } + + public SQLServerEvalVisitor(List parameters){ + this.parameters = parameters; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public boolean visit(SQLCharExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + public int incrementAndGetVariantIndex() { + return ++variantIndex; + } + + public int getVariantIndex() { + return variantIndex; + } + + public boolean visit(SQLVariantRefExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + public boolean visit(SQLBinaryOpExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + public boolean visit(SQLIntegerExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + public boolean visit(SQLNumberExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + @Override + public boolean visit(SQLCaseExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + @Override + public boolean visit(SQLInListExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + @Override + public boolean visit(SQLNullExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + @Override + public boolean visit(SQLMethodInvokeExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + @Override + public boolean visit(SQLQueryExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + + public boolean isMarkVariantIndex() { + return markVariantIndex; + } + + public void setMarkVariantIndex(boolean markVariantIndex) { + this.markVariantIndex = markVariantIndex; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java index eabcb339e9..9978755777 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java @@ -50,6 +50,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlEvalVisitorImpl; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleEvalVisitor; import com.alibaba.druid.sql.dialect.postgresql.visitor.PGEvalVisitor; +import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerEvalVisitor; import com.alibaba.druid.util.JdbcUtils; import com.alibaba.druid.wall.spi.WallVisitorUtils; import com.alibaba.druid.wall.spi.WallVisitorUtils.WallConditionContext; @@ -113,10 +114,14 @@ public class SQLEvalVisitorUtils { if (JdbcUtils.POSTGRESQL.equals(dbType)) { return new PGEvalVisitor(); } + + if (JdbcUtils.SQL_SERVER.equals(dbType)) { + return new SQLServerEvalVisitor(); + } return new SQLEvalVisitorImpl(); } - + public static boolean visit(SQLEvalVisitor visitor, SQLMethodInvokeExpr x) { String methodName = x.getMethodName().toLowerCase(); @@ -260,7 +265,7 @@ public class SQLEvalVisitorUtils { String result = strValue.trim(); x.putAttribute(EVAL_VALUE, result); - } else if ("length".equals(methodName)) { + } else if ("length".equals(methodName) || "len".equals(methodName)) { if (x.getParameters().size() != 1) { return false; } @@ -753,6 +758,11 @@ public class SQLEvalVisitorUtils { } public static boolean visit(SQLEvalVisitor visitor, SQLQueryExpr x) { + if (WallVisitorUtils.isSimpleCountTableSource(((SQLQueryExpr) x).getSubQuery())) { + x.putAttribute(EVAL_VALUE, 1); + return false; + } + if (x.getSubQuery().getQuery() instanceof SQLSelectQueryBlock) { SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) x.getSubQuery().getQuery(); @@ -809,6 +819,23 @@ public class SQLEvalVisitorUtils { } } + Object leftValue = left.getAttribute(EVAL_VALUE); + Object rightValue = right.getAttributes().get(EVAL_VALUE); + + if (x.getOperator() == SQLBinaryOperator.Like) { + if (isAlwayTrueLikePattern(x.getRight())) { + x.putAttribute(EVAL_VALUE, Boolean.TRUE); + return false; + } + } + + if (x.getOperator() == SQLBinaryOperator.NotLike) { + if (isAlwayTrueLikePattern(x)) { + x.putAttribute(EVAL_VALUE, Boolean.FALSE); + return false; + } + } + if (!left.getAttributes().containsKey(EVAL_VALUE)) { return false; } @@ -832,8 +859,6 @@ public class SQLEvalVisitorUtils { } Object value = null; - Object leftValue = left.getAttribute(EVAL_VALUE); - Object rightValue = right.getAttributes().get(EVAL_VALUE); switch (x.getOperator()) { case Add: value = add(leftValue, rightValue); @@ -872,6 +897,10 @@ public class SQLEvalVisitorUtils { value = eq(leftValue, rightValue); x.putAttribute(EVAL_VALUE, value); break; + case NotEqual: + value = !eq(leftValue, rightValue); + x.putAttribute(EVAL_VALUE, value); + break; case IsNot: value = !eq(leftValue, rightValue); x.putAttribute(EVAL_VALUE, value); @@ -918,6 +947,22 @@ public class SQLEvalVisitorUtils { return false; } + private static boolean isAlwayTrueLikePattern(SQLExpr x) { + if (x instanceof SQLCharExpr) { + String text = ((SQLCharExpr) x).getText(); + + if (text.length() >= 0) { + for (char ch : text.toCharArray()) { + if (ch != '%') { + return false; + } + } + return true; + } + } + return false; + } + public static boolean visit(SQLEvalVisitor visitor, SQLNumericLiteralExpr x) { x.getAttributes().put(EVAL_VALUE, x.getNumber()); return false; @@ -1564,5 +1609,6 @@ public class SQLEvalVisitorUtils { String regexpr = regexprBuilder.toString(); return Pattern.matches(regexpr, input); } + } diff --git a/src/main/java/com/alibaba/druid/wall/WallProvider.java b/src/main/java/com/alibaba/druid/wall/WallProvider.java index 4aa2dbc5b6..103f57d14f 100644 --- a/src/main/java/com/alibaba/druid/wall/WallProvider.java +++ b/src/main/java/com/alibaba/druid/wall/WallProvider.java @@ -60,7 +60,7 @@ public abstract class WallProvider { public WallProvider(WallConfig config){ this.config = config; } - + public WallProvider(WallConfig config, String dbType){ this.config = config; this.dbType = dbType; @@ -136,9 +136,17 @@ public abstract class WallProvider { public abstract ExportParameterVisitor createExportParameterVisitor(); public boolean checkValid(String sql) { - WallContext.createIfNotExists(dbType); - WallCheckResult result = check(sql); - return result.getViolations().isEmpty(); + WallContext originalContext = WallContext.current(); + + try { + WallContext.createIfNotExists(dbType); + WallCheckResult result = check(sql); + return result.getViolations().isEmpty(); + } finally { + if (originalContext == null) { + WallContext.clearContext(); + } + } } public void incrementCommentDeniedCount() { diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/CountTest.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/CountTest.java new file mode 100644 index 0000000000..f5bb75ec34 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/CountTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 1999-2011 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.filter.wall; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.wall.spi.WallVisitorUtils; + +public class CountTest extends TestCase { + + public void test_isTrue() throws Exception { + + Assert.assertEquals(Boolean.TRUE, WallVisitorUtils.getValue(SQLUtils.toSQLExpr("(select count(*) from t) > 0"))); + Assert.assertEquals(Boolean.TRUE, + WallVisitorUtils.getValue(SQLUtils.toSQLExpr("(select count(*) from t) >= 0"))); + Assert.assertEquals(Boolean.FALSE, + WallVisitorUtils.getValue(SQLUtils.toSQLExpr("(select count(*) from t) < 0"))); + Assert.assertEquals(Boolean.TRUE, + WallVisitorUtils.getValue(SQLUtils.toSQLExpr("NOT (select count(*) from t) < 0"))); + + // + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/LikeTest.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/LikeTest.java new file mode 100644 index 0000000000..2c64760487 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/LikeTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 1999-2011 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.filter.wall; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.wall.spi.WallVisitorUtils; + +public class LikeTest extends TestCase { + + public void test_isTrue() throws Exception { + 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 '%%'"))); + + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest2.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest2.java index d0bbfeefda..c4822f0968 100644 --- a/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest2.java +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest2.java @@ -19,7 +19,6 @@ import junit.framework.TestCase; import org.junit.Assert; -import com.alibaba.druid.wall.WallConfig; import com.alibaba.druid.wall.WallUtils; /** @@ -31,27 +30,13 @@ import com.alibaba.druid.wall.WallUtils; */ public class MySqlWallTest2 extends TestCase { - public void test_true() throws Exception { - WallConfig config = new WallConfig(); - config.setConstArithmeticAllow(true); + public void test_stuff() throws Exception { Assert.assertTrue(WallUtils.isValidateMySql(// "select * from person_qingjia " + // "where isxiao=1 " + // "and ((starttime between 2013-01-15 and 2013-01-15)" + // " or (endtime between 2013-01- 15 and 2013-01-15)" + // ") " + // - "order by xiaojiatime desc", config)); - } - - public void test_false() throws Exception { - WallConfig config = new WallConfig(); - config.setConstArithmeticAllow(true); - Assert.assertFalse(WallUtils.isValidateMySql(// - "select * from person_qingjia " + // - "where isxiao=1 " + // - "and ((starttime between 2013-01-15 and 2013-01-15)" + // - " or (endtime between 2013-01- 15 and 2013-01-15)" + // - ") " + // - "order by xiaojiatime desc", config)); + "order by xiaojiatime desc")); } }