From d847599e530d7d170d86f6ad60ffbd9ffc3b894a Mon Sep 17 00:00:00 2001 From: wenshao Date: Tue, 3 Oct 2017 14:42:18 +0800 Subject: [PATCH] select/update/delete add method 'addCondition & removeCondition'. for issue #1865 --- .../sql/ast/statement/SQLDeleteStatement.java | 82 ++++++++++ .../ast/statement/SQLSelectQueryBlock.java | 71 ++++++++ .../sql/ast/statement/SQLUpdateStatement.java | 81 ++++++++++ .../db2/ast/stmt/DB2SelectQueryBlock.java | 8 +- .../ast/statement/MySqlSelectQueryBlock.java | 3 +- .../odps/ast/OdpsSelectQueryBlock.java | 5 +- .../ast/stmt/OracleSelectQueryBlock.java | 3 +- .../ast/stmt/PGSelectQueryBlock.java | 5 + .../ast/SQLServerSelectQueryBlock.java | 5 + .../com/alibaba/druid/bvt/bug/Issue1865.java | 153 ++++++++++++++++++ 10 files changed, 409 insertions(+), 7 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/bug/Issue1865.java 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 e87eb6a2b3..7569874572 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 @@ -15,7 +15,11 @@ */ package com.alibaba.druid.sql.ast.statement; +import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.*; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExprGroup; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -210,4 +214,82 @@ public class SQLDeleteStatement extends SQLStatementImpl implements SQLReplaceab } this.with = with; } + + public void addCondition(String conditionSql) { + if (conditionSql == null || conditionSql.length() == 0) { + return; + } + + SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, dbType); + addCondition(condition); + } + + public void addCondition(SQLExpr expr) { + if (expr == null) { + return; + } + + this.setWhere(SQLBinaryOpExpr.and(where, expr)); + } + + public boolean removeCondition(String conditionSql) { + if (conditionSql == null || conditionSql.length() == 0) { + return false; + } + + SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, dbType); + + return removeCondition(condition); + } + + public boolean removeCondition(SQLExpr condition) { + if (condition == null) { + return false; + } + + if (where instanceof SQLBinaryOpExprGroup) { + SQLBinaryOpExprGroup group = (SQLBinaryOpExprGroup) where; + + int removedCount = 0; + List items = group.getItems(); + for (int i = items.size() - 1; i >= 0; i--) { + if (items.get(i).equals(condition)) { + items.remove(i); + removedCount++; + } + } + if (items.size() == 0) { + where = null; + } + + return removedCount > 0; + } + + if (where instanceof SQLBinaryOpExpr) { + SQLBinaryOpExpr binaryOpWhere = (SQLBinaryOpExpr) where; + SQLBinaryOperator operator = binaryOpWhere.getOperator(); + if (operator == SQLBinaryOperator.BooleanAnd || operator == SQLBinaryOperator.BooleanOr) { + List items = SQLBinaryOpExpr.split(binaryOpWhere); + + int removedCount = 0; + for (int i = items.size() - 1; i >= 0; i--) { + SQLExpr item = items.get(i); + if (item.equals(condition)) { + if (SQLUtils.replaceInParent(item, null)) { + removedCount++; + } + } + } + + return removedCount > 0; + } + } + + if (condition.equals(where)) { + where = null; + return true; + } + + return false; + } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectQueryBlock.java index 9084363114..fda054e429 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectQueryBlock.java @@ -55,6 +55,8 @@ public class SQLSelectQueryBlock extends SQLObjectImpl implements SQLSelectQuery protected String cachedSelectList; // optimized for SelectListCache + protected String dbType; + public SQLSelectQueryBlock(){ } @@ -580,6 +582,67 @@ public class SQLSelectQueryBlock extends SQLObjectImpl implements SQLSelectQuery this.setWhere(SQLBinaryOpExpr.and(where, expr)); } + public boolean removeCondition(String conditionSql) { + if (conditionSql == null || conditionSql.length() == 0) { + return false; + } + + SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, dbType); + + return removeCondition(condition); + } + + public boolean removeCondition(SQLExpr condition) { + if (condition == null) { + return false; + } + + if (where instanceof SQLBinaryOpExprGroup) { + SQLBinaryOpExprGroup group = (SQLBinaryOpExprGroup) where; + + int removedCount = 0; + List items = group.getItems(); + for (int i = items.size() - 1; i >= 0; i--) { + if (items.get(i).equals(condition)) { + items.remove(i); + removedCount++; + } + } + if (items.size() == 0) { + where = null; + } + + return removedCount > 0; + } + + if (where instanceof SQLBinaryOpExpr) { + SQLBinaryOpExpr binaryOpWhere = (SQLBinaryOpExpr) where; + SQLBinaryOperator operator = binaryOpWhere.getOperator(); + if (operator == SQLBinaryOperator.BooleanAnd || operator == SQLBinaryOperator.BooleanOr) { + List items = SQLBinaryOpExpr.split(binaryOpWhere); + + int removedCount = 0; + for (int i = items.size() - 1; i >= 0; i--) { + SQLExpr item = items.get(i); + if (item.equals(condition)) { + if (SQLUtils.replaceInParent(item, null)) { + removedCount++; + } + } + } + + return removedCount > 0; + } + } + + if (condition.equals(where)) { + where = null; + return true; + } + + return false; + } + public void limit(int rowCount, int offset) { SQLLimit limit = new SQLLimit(); limit.setRowCount(new SQLIntegerExpr(rowCount)); @@ -597,4 +660,12 @@ public class SQLSelectQueryBlock extends SQLObjectImpl implements SQLSelectQuery public void setCachedSelectList(String cachedSelectList) { this.cachedSelectList = cachedSelectList; } + + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUpdateStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUpdateStatement.java index 223d3d36dc..fd9b51c591 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUpdateStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUpdateStatement.java @@ -20,6 +20,9 @@ import java.util.List; import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.*; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExprGroup; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -179,4 +182,82 @@ public class SQLUpdateStatement extends SQLStatementImpl implements SQLReplaceab } this.with = with; } + + public void addCondition(String conditionSql) { + if (conditionSql == null || conditionSql.length() == 0) { + return; + } + + SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, dbType); + addCondition(condition); + } + + public void addCondition(SQLExpr expr) { + if (expr == null) { + return; + } + + this.setWhere(SQLBinaryOpExpr.and(where, expr)); + } + + public boolean removeCondition(String conditionSql) { + if (conditionSql == null || conditionSql.length() == 0) { + return false; + } + + SQLExpr condition = SQLUtils.toSQLExpr(conditionSql, dbType); + + return removeCondition(condition); + } + + public boolean removeCondition(SQLExpr condition) { + if (condition == null) { + return false; + } + + if (where instanceof SQLBinaryOpExprGroup) { + SQLBinaryOpExprGroup group = (SQLBinaryOpExprGroup) where; + + int removedCount = 0; + List items = group.getItems(); + for (int i = items.size() - 1; i >= 0; i--) { + if (items.get(i).equals(condition)) { + items.remove(i); + removedCount++; + } + } + if (items.size() == 0) { + where = null; + } + + return removedCount > 0; + } + + if (where instanceof SQLBinaryOpExpr) { + SQLBinaryOpExpr binaryOpWhere = (SQLBinaryOpExpr) where; + SQLBinaryOperator operator = binaryOpWhere.getOperator(); + if (operator == SQLBinaryOperator.BooleanAnd || operator == SQLBinaryOperator.BooleanOr) { + List items = SQLBinaryOpExpr.split(binaryOpWhere); + + int removedCount = 0; + for (int i = items.size() - 1; i >= 0; i--) { + SQLExpr item = items.get(i); + if (item.equals(condition)) { + if (SQLUtils.replaceInParent(item, null)) { + removedCount++; + } + } + } + + return removedCount > 0; + } + } + + if (condition.equals(where)) { + where = null; + return true; + } + + return false; + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2SelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2SelectQueryBlock.java index 3c355033a8..1bb0e151ec 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2SelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2SelectQueryBlock.java @@ -21,11 +21,9 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.db2.ast.DB2Object; import com.alibaba.druid.sql.dialect.db2.visitor.DB2ASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; +import com.alibaba.druid.util.JdbcConstants; public class DB2SelectQueryBlock extends SQLSelectQueryBlock implements DB2Object { - - - private Isolation isolation; private boolean forReadOnly; @@ -54,6 +52,10 @@ public class DB2SelectQueryBlock extends SQLSelectQueryBlock implements DB2Objec visitor.endVisit(this); } + public DB2SelectQueryBlock() { + dbType = JdbcConstants.DB2; + } + public Isolation getIsolation() { return isolation; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlSelectQueryBlock.java index c3a6679220..f31de05295 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlSelectQueryBlock.java @@ -26,6 +26,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlObject; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock; import com.alibaba.druid.sql.visitor.SQLASTVisitor; +import com.alibaba.druid.util.JdbcConstants; import oracle.sql.SQLUtil; public class MySqlSelectQueryBlock extends SQLSelectQueryBlock implements MySqlObject { @@ -42,7 +43,7 @@ public class MySqlSelectQueryBlock extends SQLSelectQueryBlock implements MySqlO private List hints; public MySqlSelectQueryBlock(){ - + dbType = JdbcConstants.MYSQL; } public MySqlSelectQueryBlock clone() { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsSelectQueryBlock.java index 6b2f87b3e5..03cbee45ce 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsSelectQueryBlock.java @@ -28,6 +28,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.odps.visitor.OdpsASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; +import com.alibaba.druid.util.JdbcConstants; public class OdpsSelectQueryBlock extends SQLSelectQueryBlock { @@ -36,6 +37,8 @@ public class OdpsSelectQueryBlock extends SQLSelectQueryBlock { protected List hints; public OdpsSelectQueryBlock(){ + dbType = JdbcConstants.ODPS; + distributeBy = new ArrayList(); sortBy = new ArrayList(2); } @@ -48,8 +51,6 @@ public class OdpsSelectQueryBlock extends SQLSelectQueryBlock { this.orderBy = orderBy; } - - @Override public int hashCode() { final int prime = 31; diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java index 831426eb71..08fd124c90 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java @@ -31,6 +31,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObject; import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; +import com.alibaba.druid.util.JdbcConstants; public class OracleSelectQueryBlock extends SQLSelectQueryBlock implements OracleSQLObject { @@ -72,7 +73,7 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock implements Oracl } public OracleSelectQueryBlock(){ - + dbType = JdbcConstants.ORACLE; } public ModelClause getModelClause() { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/stmt/PGSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/stmt/PGSelectQueryBlock.java index 7fceeabee5..42af9b171a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/stmt/PGSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/ast/stmt/PGSelectQueryBlock.java @@ -25,6 +25,7 @@ import com.alibaba.druid.sql.dialect.postgresql.ast.PGSQLObject; import com.alibaba.druid.sql.dialect.postgresql.ast.PGSQLObjectImpl; import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; +import com.alibaba.druid.util.JdbcConstants; public class PGSelectQueryBlock extends SQLSelectQueryBlock implements PGSQLObject{ @@ -40,6 +41,10 @@ public class PGSelectQueryBlock extends SQLSelectQueryBlock implements PGSQLObje TEMPORARY, TEMP, UNLOGGED } + public PGSelectQueryBlock() { + dbType = JdbcConstants.POSTGRESQL; + } + public IntoOption getIntoOption() { return intoOption; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerSelectQueryBlock.java index b4b8ecbcb1..dc239fc192 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerSelectQueryBlock.java @@ -20,11 +20,16 @@ import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; +import com.alibaba.druid.util.JdbcConstants; public class SQLServerSelectQueryBlock extends SQLSelectQueryBlock { private SQLServerTop top; + public SQLServerSelectQueryBlock() { + dbType = JdbcConstants.SQL_SERVER; + } + public SQLServerTop getTop() { return top; } diff --git a/src/test/java/com/alibaba/druid/bvt/bug/Issue1865.java b/src/test/java/com/alibaba/druid/bvt/bug/Issue1865.java new file mode 100644 index 0000000000..ef0e71ecce --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/bug/Issue1865.java @@ -0,0 +1,153 @@ +package com.alibaba.druid.bvt.bug; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +import com.alibaba.druid.sql.parser.SQLParserFeature; +import com.alibaba.druid.sql.parser.SQLParserUtils; +import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.util.JdbcConstants; +import junit.framework.TestCase; + +import java.util.List; + +public class Issue1865 extends TestCase { + public void test_for_select() throws Exception { + final String dbType = JdbcConstants.MYSQL; + + String sql = "select * from t where id = 2 and name = 'wenshao'"; + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatement(); + SQLSelectQueryBlock queryBlock = stmt.getSelect().getQueryBlock(); + + assertTrue(queryBlock.removeCondition("name = 'wenshao'")); + + assertEquals("SELECT *\n" + + "FROM t\n" + + "WHERE id = 2", stmt.toString()); + + assertTrue( + queryBlock.removeCondition("id = 2")); + + assertEquals("SELECT *\n" + + "FROM t", stmt.toString()); + } + + public void test_for_select_group() throws Exception { + final String dbType = JdbcConstants.MYSQL; + + String sql = "select * from t where id = 2 and name = 'wenshao'"; + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType, SQLParserFeature.EnableSQLBinaryOpExprGroup); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatement(); + SQLSelectQueryBlock queryBlock = stmt.getSelect().getQueryBlock(); + + assertTrue(queryBlock.removeCondition("name = 'wenshao'")); + + assertEquals("SELECT *\n" + + "FROM t\n" + + "WHERE id = 2", stmt.toString()); + + assertTrue( + queryBlock.removeCondition("id = 2")); + + assertEquals("SELECT *\n" + + "FROM t", stmt.toString()); + } + + public void test_for_delete() throws Exception { + final String dbType = JdbcConstants.MYSQL; + + String sql = "delete from t where id = 2 and name = 'wenshao'"; + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType); + SQLDeleteStatement stmt = (SQLDeleteStatement) parser.parseStatement(); + + assertTrue(stmt.removeCondition("name = 'wenshao'")); + + assertEquals("DELETE FROM t\n" + + "WHERE id = 2", stmt.toString()); + + assertTrue( + stmt.removeCondition("id = 2")); + + assertEquals("DELETE FROM t", stmt.toString()); + + stmt.addCondition("id = 3"); + assertEquals("DELETE FROM t\n" + + "WHERE id = 3", stmt.toString()); + } + + public void test_for_delete_group() throws Exception { + final String dbType = JdbcConstants.MYSQL; + + String sql = "delete from t where id = 2 and name = 'wenshao'"; + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType, SQLParserFeature.EnableSQLBinaryOpExprGroup); + SQLDeleteStatement stmt = (SQLDeleteStatement) parser.parseStatement(); + + assertTrue(stmt.removeCondition("name = 'wenshao'")); + + assertEquals("DELETE FROM t\n" + + "WHERE id = 2", stmt.toString()); + + assertTrue( + stmt.removeCondition("id = 2")); + + assertEquals("DELETE FROM t", stmt.toString()); + + stmt.addCondition("id = 3"); + assertEquals("DELETE FROM t\n" + + "WHERE id = 3", stmt.toString()); + } + + public void test_for_update() throws Exception { + final String dbType = JdbcConstants.MYSQL; + + String sql = "update t set val = ? where id = 2 and name = 'wenshao'"; + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType); + SQLUpdateStatement stmt = (SQLUpdateStatement) parser.parseStatement(); + + assertTrue(stmt.removeCondition("name = 'wenshao'")); + + assertEquals("UPDATE t\n" + + "SET val = ?\n" + + "WHERE id = 2", stmt.toString()); + + assertTrue( + stmt.removeCondition("id = 2")); + + assertEquals("UPDATE t\n" + + "SET val = ?", stmt.toString()); + + stmt.addCondition("id = 3"); + assertEquals("UPDATE t\n" + + "SET val = ?\n" + + "WHERE id = 3", stmt.toString()); + } + + public void test_for_update_group() throws Exception { + final String dbType = JdbcConstants.MYSQL; + + String sql = "update t set val = ? where id = 2 and name = 'wenshao'"; + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType, SQLParserFeature.EnableSQLBinaryOpExprGroup); + SQLUpdateStatement stmt = (SQLUpdateStatement) parser.parseStatement(); + + assertTrue(stmt.removeCondition("name = 'wenshao'")); + + assertEquals("UPDATE t\n" + + "SET val = ?\n" + + "WHERE id = 2", stmt.toString()); + + assertTrue( + stmt.removeCondition("id = 2")); + + assertEquals("UPDATE t\n" + + "SET val = ?", stmt.toString()); + + stmt.addCondition("id = 3"); + assertEquals("UPDATE t\n" + + "SET val = ?\n" + + "WHERE id = 3", stmt.toString()); + } +}