From a8bd3b249a30301dc47aaca5303a797a642424a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A9=E5=B0=91?= Date: Sat, 31 Mar 2012 08:40:29 +0800 Subject: [PATCH] add merge sql stat filter --- .../druid/filter/stat/MergeSqlStatFilter.java | 40 +++++ .../MySqlParameterizedOutputVisitor.java | 129 ++------------ .../OracleParameterizedOutputVisitor.java | 129 ++------------ .../visitor/PGParameterizedOutputVisitor.java | 72 ++++++++ .../druid/sql/parser/SQLParserUtils.java | 30 ++++ .../visitor/ParameterizedOutputVisitor.java | 70 ++++++++ .../ParameterizedOutputVisitorUtils.java | 166 ++++++++++++++++++ 7 files changed, 398 insertions(+), 238 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/filter/stat/MergeSqlStatFilter.java 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/MergeSqlStatFilter.java b/src/main/java/com/alibaba/druid/filter/stat/MergeSqlStatFilter.java new file mode 100644 index 0000000000..0f344fb32a --- /dev/null +++ b/src/main/java/com/alibaba/druid/filter/stat/MergeSqlStatFilter.java @@ -0,0 +1,40 @@ +package com.alibaba.druid.filter.stat; + +import com.alibaba.druid.logging.Log; +import com.alibaba.druid.logging.LogFactory; +import com.alibaba.druid.proxy.jdbc.DataSourceProxy; +import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; +import com.alibaba.druid.stat.JdbcSqlStat; + +public class MergeSqlStatFilter extends StatFilter { + + private final static Log LOG = LogFactory.getLog(MergeSqlStatFilter.class); + + private String dbType; + + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } + + @Override + public void init(DataSourceProxy dataSource) { + super.init(dataSource); + + if (this.dbType == null || this.dbType.trim().length() == 0) { + this.dbType = dataSource.getDbType(); + } + } + + public JdbcSqlStat getSqlStat(String sql) { + try { + sql = ParameterizedOutputVisitorUtils.parameterize(sql, sql); + } catch (Exception e) { + LOG.error("merge sql error", e); + } + return dataSourceStat.getSqlStat(sql); + } +} 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; + } +}