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 66ebbbbf03..49568e625b 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 @@ -16,6 +16,7 @@ package com.alibaba.druid.sql.dialect.mysql.visitor; import java.security.AccessControlException; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; @@ -148,6 +149,8 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnlockTablesStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateTableSource; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MysqlDeallocatePrepareStatement; +import com.alibaba.druid.sql.visitor.ExportParameterVisitor; +import com.alibaba.druid.sql.visitor.ExportParameterVisitorUtils; import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; import com.alibaba.druid.util.JdbcConstants; @@ -695,7 +698,12 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV public boolean visit(SQLCharExpr x) { if (this.parameterized && ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return ParameterizedOutputVisitorUtils.visit(this, x); + print('?'); + incrementReplaceCunt(); + if (this instanceof ExportParameterVisitor || this.parameters != null) { + ExportParameterVisitorUtils.exportParameter(this.parameters, x); + } + return false; } print('\''); @@ -735,7 +743,18 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV if (index >= 0 && index < parametersSize) { Object param = this.getParameters().get(index); - printParameter(param); + if (param instanceof Collection && x.getParent() instanceof SQLInListExpr) { + boolean first = true; + for (Object item : (Collection) param) { + if (!first) { + print0(", "); + } + printParameter(item); + first = false; + } + } else { + printParameter(param); + } return false; } } @@ -2922,7 +2941,12 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public boolean visit(MySqlCharExpr x) { if (parameterized && ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return ParameterizedOutputVisitorUtils.visit(this, x); + print('?'); + incrementReplaceCunt(); + if (this instanceof ExportParameterVisitor || this.parameters != null) { + ExportParameterVisitorUtils.exportParameter(this.parameters, x); + } + return false; } print0(x.toString()); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleParameterizedOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleParameterizedOutputVisitor.java index 458796567e..095f385b98 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleParameterizedOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleParameterizedOutputVisitor.java @@ -41,24 +41,12 @@ public class OracleParameterizedOutputVisitor extends OracleOutputVisitor implem super(appender, printPostSemi); } - public boolean visit(SQLInListExpr x) { - return ParameterizedOutputVisitorUtils.visit(this, x); - } - public boolean visit(SQLBinaryOpExpr x) { x = ParameterizedOutputVisitorUtils.merge(this, x); return super.visit(x); } - public boolean visit(SQLIntegerExpr x) { - if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return super.visit(x); - } - - return ParameterizedOutputVisitorUtils.visit(this, x); - } - public boolean visit(SQLNumberExpr x) { if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) { return super.visit(x); @@ -69,28 +57,4 @@ public class OracleParameterizedOutputVisitor extends OracleOutputVisitor implem return false; } - public boolean visit(SQLCharExpr x) { - if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return super.visit(x); - } - - return ParameterizedOutputVisitorUtils.visit(this, x); - } - - public boolean visit(SQLNCharExpr x) { - if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return super.visit(x); - } - - return ParameterizedOutputVisitorUtils.visit(this, x); - } - - public boolean visit(SQLNullExpr x) { - if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return super.visit(x); - } - - return ParameterizedOutputVisitorUtils.visit(this, x); - } - } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/ExportParameterVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/ExportParameterVisitor.java index 416b83b3ae..9bb40af644 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/ExportParameterVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/ExportParameterVisitor.java @@ -19,5 +19,8 @@ import java.util.List; public interface ExportParameterVisitor extends SQLASTVisitor { + boolean isParameterizedMergeInList(); + void setParameterizedMergeInList(boolean flag); + List getParameters(); } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java b/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java index 389ab818d4..bc5a356822 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java @@ -24,11 +24,8 @@ import com.alibaba.druid.sql.ast.SQLStatement; 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.SQLHexExpr; -import com.alibaba.druid.sql.ast.expr.SQLInListExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; -import com.alibaba.druid.sql.ast.expr.SQLNCharExpr; import com.alibaba.druid.sql.ast.expr.SQLNullExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; @@ -127,56 +124,6 @@ public class ParameterizedOutputVisitorUtils { return new SQLASTOutputVisitor(out, true); } - - public static boolean visit(ParameterizedVisitor v, SQLInListExpr x) { - List targetList = x.getTargetList(); - - boolean changed = true; - if (targetList.size() == 1 && targetList.get(0) instanceof SQLVariantRefExpr) { - changed = false; - } - - x.getExpr().accept(v); - - if (x.isNot()) { - v.print(v.isUppCase() ? " NOT IN (?)" : " not in (?)"); - } else { - v.print(v.isUppCase() ? " IN (?)" : " in (?)"); - } - - if (changed) { - v.incrementReplaceCunt(); - if( v instanceof ExportParameterVisitor){ - ExportParameterVisitorUtils.exportParameter(((ExportParameterVisitor)v).getParameters(), x); - } - } - - return false; - } - - public static boolean visit(ParameterizedVisitor v, SQLIntegerExpr x) { - if (!checkParameterize(x)) { - return SQLASTOutputVisitorUtils.visit(v, x); - } - - v.print('?'); - v.incrementReplaceCunt(); - - if( v instanceof ExportParameterVisitor){ - ExportParameterVisitorUtils.exportParameter(((ExportParameterVisitor)v).getParameters(), x); - } - return false; - } - - public static boolean visit(ParameterizedVisitor v, SQLCharExpr x) { - v.print('?'); - v.incrementReplaceCunt(); - if( v instanceof ExportParameterVisitor){ - ExportParameterVisitorUtils.exportParameter(((ExportParameterVisitor)v).getParameters(), x); - } - return false; - } - public static boolean checkParameterize(SQLObject x) { if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { @@ -197,51 +144,11 @@ public class ParameterizedOutputVisitorUtils { return true; } - public static boolean visit(ParameterizedVisitor v, SQLNCharExpr x) { - v.print('?'); - v.incrementReplaceCunt(); - - if( v instanceof ExportParameterVisitor){ - ExportParameterVisitorUtils.exportParameter(((ExportParameterVisitor)v).getParameters(), x); - } - return false; - } - - public static boolean visit(ParameterizedVisitor v, SQLNullExpr x) { - SQLObject parent = x.getParent(); - if (parent instanceof SQLBinaryOpExpr) { - SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) parent; - if (binaryOpExpr.getOperator() == SQLBinaryOperator.IsNot - || binaryOpExpr.getOperator() == SQLBinaryOperator.Is) { - v.print("NULL"); - return false; - } - } - - v.print('?'); - v.incrementReplaceCunt(); - - if( v instanceof ExportParameterVisitor){ - ExportParameterVisitorUtils.exportParameter(((ExportParameterVisitor)v).getParameters(), x); - } - return false; - } - public static boolean visit(ParameterizedVisitor v, SQLVariantRefExpr x) { v.print('?'); v.incrementReplaceCunt(); - - if( v instanceof ExportParameterVisitor){ - ExportParameterVisitorUtils.exportParameter(((ExportParameterVisitor)v).getParameters(), x); - } - return false; - } - - public static boolean visit(ParameterizedVisitor v, SQLHexExpr x) { - v.print('?'); - v.incrementReplaceCunt(); - - if( v instanceof ExportParameterVisitor){ + + if (v instanceof ExportParameterVisitor) { ExportParameterVisitorUtils.exportParameter(((ExportParameterVisitor)v).getParameters(), x); } return false; 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 aacc1389e2..e5c9e05337 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -55,6 +55,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet protected int replaceCount; protected boolean parameterized = false; + protected boolean parameterizedMergeInList = false; public SQLASTOutputVisitor(Appendable appender){ this.appender = appender; @@ -133,6 +134,22 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet this.indentCount += 1; } + public boolean isParameterized() { + return parameterized; + } + + public void setParameterized(boolean parameterized) { + this.parameterized = parameterized; + } + + public boolean isParameterizedMergeInList() { + return parameterizedMergeInList; + } + + public void setParameterizedMergeInList(boolean parameterizedMergeInList) { + this.parameterizedMergeInList = parameterizedMergeInList; + } + public void print(char value) { if (this.appender == null) { return; @@ -452,7 +469,12 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet public boolean visit(SQLCharExpr x) { if (this.parameterized && ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return ParameterizedOutputVisitorUtils.visit(this, x); + print('?'); + incrementReplaceCunt(); + if (this instanceof ExportParameterVisitor || this.parameters != null) { + ExportParameterVisitorUtils.exportParameter(this.parameters, x); + } + return false; } if (x.getText() == null) { @@ -504,7 +526,41 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet public boolean visit(SQLInListExpr x) { if (this.parameterized) { - return ParameterizedOutputVisitorUtils.visit(this, x); + List targetList = x.getTargetList(); + + boolean changed = true; + if (targetList.size() == 1 && targetList.get(0) instanceof SQLVariantRefExpr) { + changed = false; + } + + x.getExpr().accept(this); + + if (x.isNot()) { + print(isUppCase() ? " NOT IN (?)" : " not in (?)"); + } else { + print(isUppCase() ? " IN (?)" : " in (?)"); + } + + if (changed) { + incrementReplaceCunt(); + if(this instanceof ExportParameterVisitor || this.parameters != null) { + if (parameterizedMergeInList) { + List subList = new ArrayList(x.getTargetList().size()); + for (SQLExpr target : x.getTargetList()) { + ExportParameterVisitorUtils.exportParameter(subList, target); + } + if (subList != null) { + parameters.add(subList); + } + } else { + for (SQLExpr target : x.getTargetList()) { + ExportParameterVisitorUtils.exportParameter(this.parameters, target); + } + } + } + } + + return false; } x.getExpr().accept(this); @@ -551,7 +607,17 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet public boolean visit(SQLIntegerExpr x) { if (this.parameterized && ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return ParameterizedOutputVisitorUtils.visit(this, x); + if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) { + return SQLASTOutputVisitorUtils.visit(this, x); + } + + print('?'); + incrementReplaceCunt(); + + if(this instanceof ExportParameterVisitor || this.parameters != null){ + ExportParameterVisitorUtils.exportParameter(this.parameters, x); + } + return false; } return SQLASTOutputVisitorUtils.visit(this, x); @@ -618,7 +684,13 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet public boolean visit(SQLNCharExpr x) { if (this.parameterized && ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return ParameterizedOutputVisitorUtils.visit(this, x); + print('?'); + incrementReplaceCunt(); + + if(this instanceof ExportParameterVisitor || this.parameters != null){ + ExportParameterVisitorUtils.exportParameter(this.parameters, x); + } + return false; } if ((x.getText() == null) || (x.getText().length() == 0)) { @@ -656,7 +728,23 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet public boolean visit(SQLNullExpr x) { if (this.parameterized && ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return ParameterizedOutputVisitorUtils.visit(this, x); + SQLObject parent = x.getParent(); + if (parent instanceof SQLBinaryOpExpr) { + SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) parent; + if (binaryOpExpr.getOperator() == SQLBinaryOperator.IsNot + || binaryOpExpr.getOperator() == SQLBinaryOperator.Is) { + print("NULL"); + return false; + } + } + + print('?'); + incrementReplaceCunt(); + + if(this instanceof ExportParameterVisitor || this.parameters != null){ + ExportParameterVisitorUtils.exportParameter(this.parameters, x); + } + return false; } print0(ucase ? "NULL" : "null"); @@ -962,7 +1050,19 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet } Object param = parameters.get(index); - printParameter(param); + + if (param instanceof Collection && x.getParent() instanceof SQLInListExpr) { + boolean first = true; + for (Object item : (Collection) param) { + if (!first) { + print0(", "); + } + printParameter(item); + first = false; + } + } else { + printParameter(param); + } return false; } @@ -1398,7 +1498,13 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet @Override public boolean visit(SQLHexExpr x) { if (this.parameterized && ParameterizedOutputVisitorUtils.checkParameterize(x)) { - return ParameterizedOutputVisitorUtils.visit(this, x); + print('?'); + incrementReplaceCunt(); + + if(this instanceof ExportParameterVisitor || this.parameters != null){ + ExportParameterVisitorUtils.exportParameter(this.parameters, x); + } + return false; } print0("0x"); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/ExportParameterInTest.java b/src/test/java/com/alibaba/druid/bvt/sql/ExportParameterInTest.java new file mode 100644 index 0000000000..b58282d5ee --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/ExportParameterInTest.java @@ -0,0 +1,57 @@ +package com.alibaba.druid.bvt.sql; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; +import com.alibaba.druid.util.JdbcConstants; +import com.alibaba.fastjson.JSON; +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by wenshao on 23/11/2016. + */ +public class ExportParameterInTest extends TestCase { + String dbType = JdbcConstants.MYSQL; + + public void test_exportParameter() throws Exception { + String sql = "select * from t_user where oid = '102' and uid in (1, 2, 3)"; + + List stmtList = SQLUtils.parseStatements(sql, dbType); + + assertEquals(1, stmtList.size()); + + SQLStatement stmt = stmtList.get(0); + + StringBuilder out = new StringBuilder(); + List parameters = new ArrayList(); + SQLASTOutputVisitor visitor = SQLUtils.createOutputVisitor(out, dbType); + visitor.setParameterized(true); + visitor.setParameterizedMergeInList(true); + visitor.setParameters(parameters); + + stmt.accept(visitor); + + System.out.println(out); + System.out.println(JSON.toJSONString(parameters)); + + restore(out.toString(), parameters); + } + + public void restore(String sql, List parameters) { + List stmtList = SQLUtils.parseStatements(sql, dbType); + + assertEquals(1, stmtList.size()); + + SQLStatement stmt = stmtList.get(0); + + StringBuilder out = new StringBuilder(); + SQLASTOutputVisitor visitor = SQLUtils.createOutputVisitor(out, dbType); + visitor.setParameters(parameters); + stmt.accept(visitor); + + System.out.println(out); + } +}