From 3b79f5e99cc81b4088acea0b01dbe93ef164b63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A9=E5=B0=91?= Date: Sat, 31 Mar 2012 09:07:59 +0800 Subject: [PATCH] add merge sql stat filter --- .../alibaba/druid/filter/stat/StatFilter.java | 69 ++++++++ .../druid/pool/ha/MultiDataSource.java | 10 +- .../druid/proxy/jdbc/DataSourceProxy.java | 4 + .../druid/proxy/jdbc/DataSourceProxyImpl.java | 4 + .../MySqlParameterizedOutputVisitor.java | 129 ++------------ .../OracleParameterizedOutputVisitor.java | 129 ++------------ .../visitor/PGParameterizedOutputVisitor.java | 72 ++++++++ .../druid/sql/parser/SQLParserUtils.java | 30 ++++ .../visitor/ParameterizedOutputVisitor.java | 70 ++++++++ .../ParameterizedOutputVisitorUtils.java | 166 ++++++++++++++++++ 10 files changed, 444 insertions(+), 239 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGParameterizedOutputVisitor.java create mode 100644 src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java create mode 100644 src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitor.java create mode 100644 src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java diff --git a/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java b/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java index 00415264e6..177a0f907c 100644 --- a/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java +++ b/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java @@ -42,6 +42,7 @@ import com.alibaba.druid.proxy.jdbc.JdbcParameter; import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy; import com.alibaba.druid.proxy.jdbc.ResultSetProxy; import com.alibaba.druid.proxy.jdbc.StatementProxy; +import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; import com.alibaba.druid.stat.JdbcConnectionStat; import com.alibaba.druid.stat.JdbcDataSourceStat; import com.alibaba.druid.stat.JdbcResultSetStat; @@ -77,9 +78,21 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { // 3 seconds is slow sql protected long slowSqlMillis = 3 * 1000; + private String dbType; + + private boolean mergeSql = false; + public StatFilter(){ } + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } + public boolean isConnectionStackTraceEnable() { return connectionStackTraceEnable; } @@ -102,11 +115,67 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { return resetCount.get(); } + public boolean isMergeSql() { + return mergeSql; + } + + public void setMergeSql(boolean mergeSql) { + this.mergeSql = mergeSql; + } + + public String mergeSql(String sql) { + if (!mergeSql) { + return sql; + } + + try { + sql = ParameterizedOutputVisitorUtils.parameterize(sql, dbType); + } catch (Exception e) { + LOG.error("merge sql error", e); + } + + return sql; + } + @Override public synchronized void init(DataSourceProxy dataSource) { this.dataSource = dataSource; this.dataSourceStat = dataSource.getDataSourceStat(); + + if (this.dbType == null || this.dbType.trim().length() == 0) { + this.dbType = dataSource.getDbType(); + } + + initFromProperties(dataSource.getConnectProperties()); + initFromProperties(System.getProperties()); + } + + private void initFromProperties(Properties properties) { + if (properties == null) { + return; + } + + { + String property = properties.getProperty("druid.stat.mergeSql"); + if ("true".equals(property)) { + this.mergeSql = true; + } else if ("false".equals(property)) { + this.mergeSql = false; + } + } + + { + String property = properties.getProperty("druid.stat.slowSqlMillis"); + if (property != null && property.trim().length() > 0) { + property = property.trim(); + try { + this.slowSqlMillis = Long.parseLong(property); + } catch (Exception e) { + LOG.error("property 'druid.stat.slowSqlMillis' format error"); + } + } + } } @Override diff --git a/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java b/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java index 4725b44cb0..dcccb6c6cf 100644 --- a/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java @@ -86,6 +86,8 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc private JdbcDataSourceStat dataSourceStat; + protected Properties connectionProperties = new Properties(); + public long getMaxWaitMillis() { return maxWaitMillis; } @@ -171,7 +173,7 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc if (inited) { return; } - + dataSourceStat = new JdbcDataSourceStat(name, null); this.balancer.init(this); @@ -501,4 +503,10 @@ public class MultiDataSource extends DataSourceAdapter implements MultiDataSourc public JdbcDataSourceStat getDataSourceStat() { return dataSourceStat; } + + @Override + public Properties getConnectProperties() { + return connectionProperties; + } + } diff --git a/src/main/java/com/alibaba/druid/proxy/jdbc/DataSourceProxy.java b/src/main/java/com/alibaba/druid/proxy/jdbc/DataSourceProxy.java index 5f4b09123c..960763c774 100644 --- a/src/main/java/com/alibaba/druid/proxy/jdbc/DataSourceProxy.java +++ b/src/main/java/com/alibaba/druid/proxy/jdbc/DataSourceProxy.java @@ -17,6 +17,7 @@ package com.alibaba.druid.proxy.jdbc; import java.sql.Driver; import java.util.List; +import java.util.Properties; import com.alibaba.druid.filter.Filter; import com.alibaba.druid.stat.JdbcDataSourceStat; @@ -25,6 +26,7 @@ import com.alibaba.druid.stat.JdbcDataSourceStat; * @author wenshao */ public interface DataSourceProxy { + JdbcDataSourceStat getDataSourceStat(); String getName(); @@ -46,4 +48,6 @@ public interface DataSourceProxy { long createResultSetId(); long createTransactionId(); + + Properties getConnectProperties(); } diff --git a/src/main/java/com/alibaba/druid/proxy/jdbc/DataSourceProxyImpl.java b/src/main/java/com/alibaba/druid/proxy/jdbc/DataSourceProxyImpl.java index c99479f947..4cb9bcc901 100644 --- a/src/main/java/com/alibaba/druid/proxy/jdbc/DataSourceProxyImpl.java +++ b/src/main/java/com/alibaba/druid/proxy/jdbc/DataSourceProxyImpl.java @@ -183,6 +183,10 @@ public class DataSourceProxyImpl implements DataSourceProxy, DataSourceProxyImpl return properties.toString(); } + public Properties getConnectProperties() { + return properties; + } + public CompositeDataSupport getCompositeData() throws JMException { StatFilter statFilter = null; JdbcDataSourceStat stat = null; diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlParameterizedOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlParameterizedOutputVisitor.java index 24a49130f7..5f5b5d3ccb 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlParameterizedOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlParameterizedOutputVisitor.java @@ -1,40 +1,31 @@ package com.alibaba.druid.sql.dialect.mysql.visitor; -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.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.SQLNumberExpr; -import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; +import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; public class MySqlParameterizedOutputVisitor extends MySqlOutputVisitor { - private static final String ATTR_PARAMS_SKIP = "_params.skip_"; + public MySqlParameterizedOutputVisitor() { + this(new StringBuilder()); + } public MySqlParameterizedOutputVisitor(Appendable appender){ super(appender); } public boolean visit(SQLInListExpr x) { - x.getExpr().accept(this); - - if (x.isNot()) { - print(" NOT IN (?)"); - } else { - print(" IN (?)"); - } - - return false; + return ParameterizedOutputVisitorUtils.visit(this, x); } public boolean visit(SQLBinaryOpExpr x) { - x = merge(x); + x = ParameterizedOutputVisitorUtils.merge(x); return super.visit(x); } @@ -44,108 +35,8 @@ public class MySqlParameterizedOutputVisitor extends MySqlOutputVisitor { return false; } - public SQLBinaryOpExpr merge(SQLBinaryOpExpr x) { - if (x.getLeft() instanceof SQLLiteralExpr && x.getRight() instanceof SQLLiteralExpr) { - if (x.getOperator() == SQLBinaryOperator.Equality || x.getOperator() == SQLBinaryOperator.NotEqual) { - x.getLeft().getAttributes().put(ATTR_PARAMS_SKIP, true); - x.getRight().getAttributes().put(ATTR_PARAMS_SKIP, true); - } - return x; - } - - if (x.getRight() instanceof SQLLiteralExpr) { - x = new SQLBinaryOpExpr(x.getLeft(), x.getOperator(), new SQLVariantRefExpr("?")); - } - - if (x.getLeft() instanceof SQLLiteralExpr) { - x = new SQLBinaryOpExpr(new SQLVariantRefExpr("?"), x.getOperator(), x.getRight()); - } - - for (;;) { - if (x.getRight() instanceof SQLBinaryOpExpr) { - if (x.getLeft() instanceof SQLBinaryOpExpr) { - SQLBinaryOpExpr leftBinaryExpr = (SQLBinaryOpExpr) x.getLeft(); - if (leftBinaryExpr.getRight().equals(x.getRight())) { - x = leftBinaryExpr; - continue; - } - } - x = new SQLBinaryOpExpr(x.getLeft(), x.getOperator(), merge((SQLBinaryOpExpr) x.getRight())); - } - - break; - } - - if (x.getLeft() instanceof SQLBinaryOpExpr) { - x = new SQLBinaryOpExpr(merge((SQLBinaryOpExpr) x.getLeft()), x.getOperator(), x.getRight()); - } - - // ID = ? OR ID = ? => ID = ? - if (x.getOperator() == SQLBinaryOperator.BooleanOr) { - if ((x.getLeft() instanceof SQLBinaryOpExpr) && (x.getRight() instanceof SQLBinaryOpExpr)) { - SQLBinaryOpExpr left = (SQLBinaryOpExpr) x.getLeft(); - SQLBinaryOpExpr right = (SQLBinaryOpExpr) x.getRight(); - - if (mergeEqual(left, right)) { - return left; - } - - if (isLiteralExpr(left.getLeft()) && left.getOperator() == SQLBinaryOperator.BooleanOr) { - if (mergeEqual(left.getRight(), right)) { - return left; - } - } - } - } - - return x; - } - - private boolean mergeEqual(SQLExpr a, SQLExpr b) { - if (!(a instanceof SQLBinaryOpExpr)) { - return false; - } - if (!(b instanceof SQLBinaryOpExpr)) { - return false; - } - - SQLBinaryOpExpr binaryA = (SQLBinaryOpExpr) a; - SQLBinaryOpExpr binaryB = (SQLBinaryOpExpr) b; - - if (binaryA.getOperator() != SQLBinaryOperator.Equality) { - return false; - } - - if (binaryB.getOperator() != SQLBinaryOperator.Equality) { - return false; - } - - if (!(binaryA.getRight() instanceof SQLLiteralExpr || binaryA.getRight() instanceof SQLVariantRefExpr)) { - return false; - } - - if (!(binaryB.getRight() instanceof SQLLiteralExpr || binaryB.getRight() instanceof SQLVariantRefExpr)) { - return false; - } - - return binaryA.getLeft().toString().equals(binaryB.getLeft().toString()); - } - - private boolean isLiteralExpr(SQLExpr expr) { - if (expr instanceof SQLLiteralExpr) { - return true; - } - - if (expr instanceof SQLBinaryOpExpr) { - SQLBinaryOpExpr binary = (SQLBinaryOpExpr) expr; - return isLiteralExpr(binary.getLeft()) && isLiteralExpr(binary.getRight()); - } - - return false; - } - public boolean visit(SQLIntegerExpr x) { - if (Boolean.TRUE.equals(x.getAttribute(ATTR_PARAMS_SKIP))) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { return super.visit(x); } @@ -154,7 +45,7 @@ public class MySqlParameterizedOutputVisitor extends MySqlOutputVisitor { } public boolean visit(SQLNumberExpr x) { - if (Boolean.TRUE.equals(x.getAttribute(ATTR_PARAMS_SKIP))) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { return super.visit(x); } @@ -163,7 +54,7 @@ public class MySqlParameterizedOutputVisitor extends MySqlOutputVisitor { } public boolean visit(SQLCharExpr x) { - if (Boolean.TRUE.equals(x.getAttribute(ATTR_PARAMS_SKIP))) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { return super.visit(x); } @@ -172,7 +63,7 @@ public class MySqlParameterizedOutputVisitor extends MySqlOutputVisitor { } public boolean visit(SQLNCharExpr x) { - if (Boolean.TRUE.equals(x.getAttribute(ATTR_PARAMS_SKIP))) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { return super.visit(x); } 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 fc0ca852a1..2d91c21268 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 @@ -1,20 +1,19 @@ package com.alibaba.druid.sql.dialect.oracle.visitor; -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.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.SQLNumberExpr; -import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; public class OracleParameterizedOutputVisitor extends OracleOutputVisitor { - private static final String ATTR_PARAMS_SKIP = "_params.skip_"; + public OracleParameterizedOutputVisitor() { + this (new StringBuilder()); + } public OracleParameterizedOutputVisitor(Appendable appender){ super(appender); @@ -25,19 +24,11 @@ public class OracleParameterizedOutputVisitor extends OracleOutputVisitor { } public boolean visit(SQLInListExpr x) { - x.getExpr().accept(this); - - if (x.isNot()) { - print(" NOT IN (?)"); - } else { - print(" IN (?)"); - } - - return false; + return ParameterizedOutputVisitorUtils.visit(this, x); } public boolean visit(SQLBinaryOpExpr x) { - x = merge(x); + x = ParameterizedOutputVisitorUtils.merge(x); return super.visit(x); } @@ -47,108 +38,8 @@ public class OracleParameterizedOutputVisitor extends OracleOutputVisitor { return false; } - public SQLBinaryOpExpr merge(SQLBinaryOpExpr x) { - if (x.getLeft() instanceof SQLLiteralExpr && x.getRight() instanceof SQLLiteralExpr) { - if (x.getOperator() == SQLBinaryOperator.Equality || x.getOperator() == SQLBinaryOperator.NotEqual) { - x.getLeft().getAttributes().put(ATTR_PARAMS_SKIP, true); - x.getRight().getAttributes().put(ATTR_PARAMS_SKIP, true); - } - return x; - } - - if (x.getRight() instanceof SQLLiteralExpr) { - x = new SQLBinaryOpExpr(x.getLeft(), x.getOperator(), new SQLVariantRefExpr("?")); - } - - if (x.getLeft() instanceof SQLLiteralExpr) { - x = new SQLBinaryOpExpr(new SQLVariantRefExpr("?"), x.getOperator(), x.getRight()); - } - - for (;;) { - if (x.getRight() instanceof SQLBinaryOpExpr) { - if (x.getLeft() instanceof SQLBinaryOpExpr) { - SQLBinaryOpExpr leftBinaryExpr = (SQLBinaryOpExpr) x.getLeft(); - if (leftBinaryExpr.getRight().equals(x.getRight())) { - x = leftBinaryExpr; - continue; - } - } - x = new SQLBinaryOpExpr(x.getLeft(), x.getOperator(), merge((SQLBinaryOpExpr) x.getRight())); - } - - break; - } - - if (x.getLeft() instanceof SQLBinaryOpExpr) { - x = new SQLBinaryOpExpr(merge((SQLBinaryOpExpr) x.getLeft()), x.getOperator(), x.getRight()); - } - - // ID = ? OR ID = ? => ID = ? - if (x.getOperator() == SQLBinaryOperator.BooleanOr) { - if ((x.getLeft() instanceof SQLBinaryOpExpr) && (x.getRight() instanceof SQLBinaryOpExpr)) { - SQLBinaryOpExpr left = (SQLBinaryOpExpr) x.getLeft(); - SQLBinaryOpExpr right = (SQLBinaryOpExpr) x.getRight(); - - if (mergeEqual(left, right)) { - return left; - } - - if (isLiteralExpr(left.getLeft()) && left.getOperator() == SQLBinaryOperator.BooleanOr) { - if (mergeEqual(left.getRight(), right)) { - return left; - } - } - } - } - - return x; - } - - private boolean mergeEqual(SQLExpr a, SQLExpr b) { - if (!(a instanceof SQLBinaryOpExpr)) { - return false; - } - if (!(b instanceof SQLBinaryOpExpr)) { - return false; - } - - SQLBinaryOpExpr binaryA = (SQLBinaryOpExpr) a; - SQLBinaryOpExpr binaryB = (SQLBinaryOpExpr) b; - - if (binaryA.getOperator() != SQLBinaryOperator.Equality) { - return false; - } - - if (binaryB.getOperator() != SQLBinaryOperator.Equality) { - return false; - } - - if (!(binaryA.getRight() instanceof SQLLiteralExpr || binaryA.getRight() instanceof SQLVariantRefExpr)) { - return false; - } - - if (!(binaryB.getRight() instanceof SQLLiteralExpr || binaryB.getRight() instanceof SQLVariantRefExpr)) { - return false; - } - - return binaryA.getLeft().toString().equals(binaryB.getLeft().toString()); - } - - private boolean isLiteralExpr(SQLExpr expr) { - if (expr instanceof SQLLiteralExpr) { - return true; - } - - if (expr instanceof SQLBinaryOpExpr) { - SQLBinaryOpExpr binary = (SQLBinaryOpExpr) expr; - return isLiteralExpr(binary.getLeft()) && isLiteralExpr(binary.getRight()); - } - - return false; - } - public boolean visit(SQLIntegerExpr x) { - if (Boolean.TRUE.equals(x.getAttribute(ATTR_PARAMS_SKIP))) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { return super.visit(x); } @@ -157,7 +48,7 @@ public class OracleParameterizedOutputVisitor extends OracleOutputVisitor { } public boolean visit(SQLNumberExpr x) { - if (Boolean.TRUE.equals(x.getAttribute(ATTR_PARAMS_SKIP))) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { return super.visit(x); } @@ -166,7 +57,7 @@ public class OracleParameterizedOutputVisitor extends OracleOutputVisitor { } public boolean visit(SQLCharExpr x) { - if (Boolean.TRUE.equals(x.getAttribute(ATTR_PARAMS_SKIP))) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { return super.visit(x); } @@ -175,7 +66,7 @@ public class OracleParameterizedOutputVisitor extends OracleOutputVisitor { } public boolean visit(SQLNCharExpr x) { - if (Boolean.TRUE.equals(x.getAttribute(ATTR_PARAMS_SKIP))) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { return super.visit(x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGParameterizedOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGParameterizedOutputVisitor.java new file mode 100644 index 0000000000..bb8d8422a0 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/visitor/PGParameterizedOutputVisitor.java @@ -0,0 +1,72 @@ +package com.alibaba.druid.sql.dialect.postgresql.visitor; + +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +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.SQLNCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLNullExpr; +import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; +import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; + +public class PGParameterizedOutputVisitor extends PGOutputVisitor { + public PGParameterizedOutputVisitor() { + this (new StringBuilder()); + } + + public PGParameterizedOutputVisitor(Appendable appender){ + super(appender); + } + + public boolean visit(SQLInListExpr x) { + return ParameterizedOutputVisitorUtils.visit(this, x); + } + + public boolean visit(SQLBinaryOpExpr x) { + x = ParameterizedOutputVisitorUtils.merge(x); + + return super.visit(x); + } + + public boolean visit(SQLNullExpr x) { + print('?'); + return false; + } + + public boolean visit(SQLIntegerExpr x) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { + return super.visit(x); + } + + print('?'); + return false; + } + + public boolean visit(SQLNumberExpr x) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { + return super.visit(x); + } + + print('?'); + return false; + } + + public boolean visit(SQLCharExpr x) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { + return super.visit(x); + } + + print('?'); + return false; + } + + public boolean visit(SQLNCharExpr x) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { + return super.visit(x); + } + + print('?'); + return false; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java new file mode 100644 index 0000000000..f19c203df5 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java @@ -0,0 +1,30 @@ +package com.alibaba.druid.sql.parser; + +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.postgresql.parser.PGSQLStatementParser; +import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import com.alibaba.druid.util.JdbcUtils; + + +public class SQLParserUtils { + public static SQLStatementParser createSQLStatementParser(String sql, String dbType) { + if (JdbcUtils.ORACLE.equals(dbType)) { + return new OracleStatementParser(sql); + } + + if (JdbcUtils.MYSQL.equals(dbType)) { + return new MySqlStatementParser(sql); + } + + if (JdbcUtils.POSTGRESQL.equals(dbType)) { + return new PGSQLStatementParser(sql); + } + + if (JdbcUtils.SQL_SERVER.equals(dbType)) { + return new SQLServerStatementParser(sql); + } + + return new SQLStatementParser(sql); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitor.java new file mode 100644 index 0000000000..7ce5a4df5c --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitor.java @@ -0,0 +1,70 @@ +package com.alibaba.druid.sql.visitor; + +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +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.SQLNCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLNullExpr; +import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; + +public class ParameterizedOutputVisitor extends SQLASTOutputVisitor { + public ParameterizedOutputVisitor() { + this (new StringBuilder()); + } + + public ParameterizedOutputVisitor(Appendable appender){ + super(appender); + } + + public boolean visit(SQLInListExpr x) { + return ParameterizedOutputVisitorUtils.visit(this, x); + } + + public boolean visit(SQLBinaryOpExpr x) { + x = ParameterizedOutputVisitorUtils.merge(x); + + return super.visit(x); + } + + public boolean visit(SQLNullExpr x) { + print('?'); + return false; + } + + public boolean visit(SQLIntegerExpr x) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { + return super.visit(x); + } + + print('?'); + return false; + } + + public boolean visit(SQLNumberExpr x) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { + return super.visit(x); + } + + print('?'); + return false; + } + + public boolean visit(SQLCharExpr x) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { + return super.visit(x); + } + + print('?'); + return false; + } + + public boolean visit(SQLNCharExpr x) { + if (Boolean.TRUE.equals(x.getAttribute(ParameterizedOutputVisitorUtils.ATTR_PARAMS_SKIP))) { + return super.visit(x); + } + + print('?'); + return false; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java b/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java new file mode 100644 index 0000000000..c5cd686420 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java @@ -0,0 +1,166 @@ +package com.alibaba.druid.sql.visitor; + +import java.util.List; + +import junit.framework.Assert; + +import com.alibaba.druid.sql.ast.SQLExpr; +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.SQLInListExpr; +import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlParameterizedOutputVisitor; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleParameterizedOutputVisitor; +import com.alibaba.druid.sql.dialect.postgresql.visitor.PGParameterizedOutputVisitor; +import com.alibaba.druid.sql.parser.SQLParserUtils; +import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.util.JdbcUtils; + +public class ParameterizedOutputVisitorUtils { + + public static final String ATTR_PARAMS_SKIP = "druid.parameterized.skip"; + + public static String parameterize(String sql, String dbType) { + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + StringBuilder out = new StringBuilder(); + SQLASTOutputVisitor visitor = createParameterizedOutputVisitor(out, dbType); + statemen.accept(visitor); + + return out.toString(); + } + + public static SQLASTOutputVisitor createParameterizedOutputVisitor(Appendable out, String dbType) { + if (JdbcUtils.ORACLE.equals(dbType)) { + return new OracleParameterizedOutputVisitor(); + } + + if (JdbcUtils.MYSQL.equals(dbType)) { + return new MySqlParameterizedOutputVisitor(); + } + + if (JdbcUtils.POSTGRESQL.equals(dbType)) { + return new PGParameterizedOutputVisitor(); + } + + return new ParameterizedOutputVisitor(); + } + + public static boolean visit(SQLASTOutputVisitor v, SQLInListExpr x) { + x.getExpr().accept(v); + + if (x.isNot()) { + v.print(" NOT IN (?)"); + } else { + v.print(" IN (?)"); + } + + return false; + } + + public static SQLBinaryOpExpr merge(SQLBinaryOpExpr x) { + if (x.getLeft() instanceof SQLLiteralExpr && x.getRight() instanceof SQLLiteralExpr) { + if (x.getOperator() == SQLBinaryOperator.Equality || x.getOperator() == SQLBinaryOperator.NotEqual) { + x.getLeft().getAttributes().put(ATTR_PARAMS_SKIP, true); + x.getRight().getAttributes().put(ATTR_PARAMS_SKIP, true); + } + return x; + } + + if (x.getRight() instanceof SQLLiteralExpr) { + x = new SQLBinaryOpExpr(x.getLeft(), x.getOperator(), new SQLVariantRefExpr("?")); + } + + if (x.getLeft() instanceof SQLLiteralExpr) { + x = new SQLBinaryOpExpr(new SQLVariantRefExpr("?"), x.getOperator(), x.getRight()); + } + + for (;;) { + if (x.getRight() instanceof SQLBinaryOpExpr) { + if (x.getLeft() instanceof SQLBinaryOpExpr) { + SQLBinaryOpExpr leftBinaryExpr = (SQLBinaryOpExpr) x.getLeft(); + if (leftBinaryExpr.getRight().equals(x.getRight())) { + x = leftBinaryExpr; + continue; + } + } + x = new SQLBinaryOpExpr(x.getLeft(), x.getOperator(), merge((SQLBinaryOpExpr) x.getRight())); + } + + break; + } + + if (x.getLeft() instanceof SQLBinaryOpExpr) { + x = new SQLBinaryOpExpr(merge((SQLBinaryOpExpr) x.getLeft()), x.getOperator(), x.getRight()); + } + + // ID = ? OR ID = ? => ID = ? + if (x.getOperator() == SQLBinaryOperator.BooleanOr) { + if ((x.getLeft() instanceof SQLBinaryOpExpr) && (x.getRight() instanceof SQLBinaryOpExpr)) { + SQLBinaryOpExpr left = (SQLBinaryOpExpr) x.getLeft(); + SQLBinaryOpExpr right = (SQLBinaryOpExpr) x.getRight(); + + if (mergeEqual(left, right)) { + return left; + } + + if (isLiteralExpr(left.getLeft()) && left.getOperator() == SQLBinaryOperator.BooleanOr) { + if (mergeEqual(left.getRight(), right)) { + return left; + } + } + } + } + + return x; + } + + private static boolean mergeEqual(SQLExpr a, SQLExpr b) { + if (!(a instanceof SQLBinaryOpExpr)) { + return false; + } + if (!(b instanceof SQLBinaryOpExpr)) { + return false; + } + + SQLBinaryOpExpr binaryA = (SQLBinaryOpExpr) a; + SQLBinaryOpExpr binaryB = (SQLBinaryOpExpr) b; + + if (binaryA.getOperator() != SQLBinaryOperator.Equality) { + return false; + } + + if (binaryB.getOperator() != SQLBinaryOperator.Equality) { + return false; + } + + if (!(binaryA.getRight() instanceof SQLLiteralExpr || binaryA.getRight() instanceof SQLVariantRefExpr)) { + return false; + } + + if (!(binaryB.getRight() instanceof SQLLiteralExpr || binaryB.getRight() instanceof SQLVariantRefExpr)) { + return false; + } + + return binaryA.getLeft().toString().equals(binaryB.getLeft().toString()); + } + + private static boolean isLiteralExpr(SQLExpr expr) { + if (expr instanceof SQLLiteralExpr) { + return true; + } + + if (expr instanceof SQLBinaryOpExpr) { + SQLBinaryOpExpr binary = (SQLBinaryOpExpr) expr; + return isLiteralExpr(binary.getLeft()) && isLiteralExpr(binary.getRight()); + } + + return false; + } +}