diff --git a/.gitignore b/.gitignore index a9cb22fcc2..2001545daa 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,5 @@ out/ *.ipr *.iws -*.iml -/bin -/target +*.iml +/bin \ No newline at end of file diff --git a/pom.xml b/pom.xml index f8a5c5f36a..364c8e9552 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.alibaba druid - 1.0.5 + 1.0.7 jar druid @@ -310,6 +310,33 @@ + + + mac-profile-oracle-jdk + + false + + ${java.home}/../lib/jconsole.jar + + + + + com.alibaba + jconsole + 1.8.0 + system + ${java.home}/../lib/jconsole.jar + + + + com.alibaba + tools + 1.8.0 + system + ${java.home}/../lib/tools.jar + + + @@ -439,13 +466,14 @@ provided + junit junit diff --git a/src/main/java/com/alibaba/druid/VERSION.java b/src/main/java/com/alibaba/druid/VERSION.java index 69c82e3ce9..3cb120c9a8 100644 --- a/src/main/java/com/alibaba/druid/VERSION.java +++ b/src/main/java/com/alibaba/druid/VERSION.java @@ -19,7 +19,7 @@ public final class VERSION { public final static int MajorVersion = 1; public final static int MinorVersion = 0; - public final static int RevisionVersion = 5; + public final static int RevisionVersion = 7; public static String getVersionNumber() { return VERSION.MajorVersion + "." + VERSION.MinorVersion + "." + VERSION.RevisionVersion; diff --git a/src/main/java/com/alibaba/druid/filter/Filter.java b/src/main/java/com/alibaba/druid/filter/Filter.java index a38eaa5b30..5d39d82fb6 100644 --- a/src/main/java/com/alibaba/druid/filter/Filter.java +++ b/src/main/java/com/alibaba/druid/filter/Filter.java @@ -15,6 +15,17 @@ */ package com.alibaba.druid.filter; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.pool.DruidPooledConnection; +import com.alibaba.druid.proxy.jdbc.CallableStatementProxy; +import com.alibaba.druid.proxy.jdbc.ClobProxy; +import com.alibaba.druid.proxy.jdbc.ConnectionProxy; +import com.alibaba.druid.proxy.jdbc.DataSourceProxy; +import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy; +import com.alibaba.druid.proxy.jdbc.ResultSetMetaDataProxy; +import com.alibaba.druid.proxy.jdbc.ResultSetProxy; +import com.alibaba.druid.proxy.jdbc.StatementProxy; + import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; @@ -44,17 +55,6 @@ import java.sql.Wrapper; import java.util.Calendar; import java.util.Properties; -import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.pool.DruidPooledConnection; -import com.alibaba.druid.proxy.jdbc.CallableStatementProxy; -import com.alibaba.druid.proxy.jdbc.ClobProxy; -import com.alibaba.druid.proxy.jdbc.ConnectionProxy; -import com.alibaba.druid.proxy.jdbc.DataSourceProxy; -import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy; -import com.alibaba.druid.proxy.jdbc.ResultSetMetaDataProxy; -import com.alibaba.druid.proxy.jdbc.ResultSetProxy; -import com.alibaba.druid.proxy.jdbc.StatementProxy; - /** * @author wenshao */ @@ -62,7 +62,7 @@ public interface Filter extends Wrapper { void init(DataSourceProxy dataSource); - void destory(); + void destroy(); void configFromProperties(Properties properties); diff --git a/src/main/java/com/alibaba/druid/filter/FilterAdapter.java b/src/main/java/com/alibaba/druid/filter/FilterAdapter.java index 2284057e0d..a5b648dc87 100644 --- a/src/main/java/com/alibaba/druid/filter/FilterAdapter.java +++ b/src/main/java/com/alibaba/druid/filter/FilterAdapter.java @@ -15,6 +15,18 @@ */ package com.alibaba.druid.filter; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.pool.DruidPooledConnection; +import com.alibaba.druid.proxy.jdbc.CallableStatementProxy; +import com.alibaba.druid.proxy.jdbc.ClobProxy; +import com.alibaba.druid.proxy.jdbc.ConnectionProxy; +import com.alibaba.druid.proxy.jdbc.DataSourceProxy; +import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy; +import com.alibaba.druid.proxy.jdbc.ResultSetMetaDataProxy; +import com.alibaba.druid.proxy.jdbc.ResultSetProxy; +import com.alibaba.druid.proxy.jdbc.StatementProxy; + +import javax.management.NotificationBroadcasterSupport; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; @@ -40,19 +52,6 @@ import java.util.Calendar; import java.util.Map; import java.util.Properties; -import javax.management.NotificationBroadcasterSupport; - -import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.pool.DruidPooledConnection; -import com.alibaba.druid.proxy.jdbc.CallableStatementProxy; -import com.alibaba.druid.proxy.jdbc.ClobProxy; -import com.alibaba.druid.proxy.jdbc.ConnectionProxy; -import com.alibaba.druid.proxy.jdbc.DataSourceProxy; -import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy; -import com.alibaba.druid.proxy.jdbc.ResultSetMetaDataProxy; -import com.alibaba.druid.proxy.jdbc.ResultSetProxy; -import com.alibaba.druid.proxy.jdbc.StatementProxy; - /** * 提供JdbcFilter的基本实现,使得实现一个JdbcFilter更容易。 * @@ -65,7 +64,7 @@ public abstract class FilterAdapter extends NotificationBroadcasterSupport imple } @Override - public void destory() { + public void destroy() { } diff --git a/src/main/java/com/alibaba/druid/mock/handler/MySqlMockExecuteHandlerImpl.java b/src/main/java/com/alibaba/druid/mock/handler/MySqlMockExecuteHandlerImpl.java index 2afd376938..e7bee2b280 100644 --- a/src/main/java/com/alibaba/druid/mock/handler/MySqlMockExecuteHandlerImpl.java +++ b/src/main/java/com/alibaba/druid/mock/handler/MySqlMockExecuteHandlerImpl.java @@ -27,6 +27,7 @@ import com.alibaba.druid.mock.MockResultSetMetaData; import com.alibaba.druid.mock.MockStatementBase; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; @@ -142,8 +143,8 @@ public class MySqlMockExecuteHandlerImpl implements MockExecuteHandler { } else if (expr instanceof SQLNCharExpr) { row[i] = ((SQLNCharExpr) expr).getText(); column.setColumnType(Types.NVARCHAR); - } else if (expr instanceof MySqlBooleanExpr) { - row[i] = ((MySqlBooleanExpr) expr).getValue(); + } else if (expr instanceof SQLBooleanExpr) { + row[i] = ((SQLBooleanExpr) expr).getValue(); column.setColumnType(Types.NVARCHAR); } else if (expr instanceof SQLNullExpr) { row[i] = null; diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 3e1b126513..d746716083 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -118,6 +118,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements protected volatile int minIdle = DEFAULT_MIN_IDLE; protected volatile int maxIdle = DEFAULT_MAX_IDLE; protected volatile long maxWait = DEFAULT_MAX_WAIT; + protected int notFullTimeoutRetryCount = 0; protected volatile String validationQuery = DEFAULT_VALIDATION_QUERY; protected volatile int validationQueryTimeout = -1; @@ -892,6 +893,15 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements this.maxWait = maxWaitMillis; } + + public int getNotFullTimeoutRetryCount() { + return notFullTimeoutRetryCount; + } + + + public void setNotFullTimeoutRetryCount(int notFullTimeoutRetryCount) { + this.notFullTimeoutRetryCount = notFullTimeoutRetryCount; + } public int getMinIdle() { return minIdle; @@ -1177,7 +1187,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements String[] filterArray = filters.split("\\,"); for (String item : filterArray) { - FilterManager.loadFilter(this.filters, item); + FilterManager.loadFilter(this.filters, item.trim()); } } diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 96190fb7a1..f21df11059 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -1,2396 +1,2534 @@ -/* - * Copyright 1999-2011 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.druid.pool; - -import static com.alibaba.druid.util.Utils.getBoolean; - -import java.io.Closeable; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.ConcurrentModificationException; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.ServiceLoader; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import javax.management.JMException; -import javax.management.ObjectName; -import javax.naming.NamingException; -import javax.naming.Reference; -import javax.naming.Referenceable; -import javax.naming.StringRefAddr; -import javax.sql.ConnectionEvent; -import javax.sql.ConnectionEventListener; -import javax.sql.ConnectionPoolDataSource; -import javax.sql.PooledConnection; - -import com.alibaba.druid.Constants; -import com.alibaba.druid.TransactionTimeoutException; -import com.alibaba.druid.VERSION; -import com.alibaba.druid.filter.AutoLoad; -import com.alibaba.druid.filter.Filter; -import com.alibaba.druid.filter.FilterChainImpl; -import com.alibaba.druid.mock.MockDriver; -import com.alibaba.druid.pool.DruidPooledPreparedStatement.PreparedStatementKey; -import com.alibaba.druid.pool.vendor.DB2ExceptionSorter; -import com.alibaba.druid.pool.vendor.InformixExceptionSorter; -import com.alibaba.druid.pool.vendor.MSSQLValidConnectionChecker; -import com.alibaba.druid.pool.vendor.MockExceptionSorter; -import com.alibaba.druid.pool.vendor.MySqlExceptionSorter; -import com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker; -import com.alibaba.druid.pool.vendor.OracleExceptionSorter; -import com.alibaba.druid.pool.vendor.OracleValidConnectionChecker; -import com.alibaba.druid.pool.vendor.SybaseExceptionSorter; -import com.alibaba.druid.proxy.DruidDriver; -import com.alibaba.druid.proxy.jdbc.DataSourceProxyConfig; -import com.alibaba.druid.proxy.jdbc.TransactionInfo; -import com.alibaba.druid.sql.ast.SQLStatement; -import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; -import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; -import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; -import com.alibaba.druid.sql.parser.SQLParserUtils; -import com.alibaba.druid.sql.parser.SQLStatementParser; -import com.alibaba.druid.stat.DruidDataSourceStatManager; -import com.alibaba.druid.stat.JdbcDataSourceStat; -import com.alibaba.druid.stat.JdbcSqlStat; -import com.alibaba.druid.stat.JdbcSqlStatValue; -import com.alibaba.druid.support.logging.Log; -import com.alibaba.druid.support.logging.LogFactory; -import com.alibaba.druid.util.JMXUtils; -import com.alibaba.druid.util.JdbcConstants; -import com.alibaba.druid.util.JdbcUtils; -import com.alibaba.druid.util.StringUtils; -import com.alibaba.druid.util.Utils; -import com.alibaba.druid.wall.WallFilter; -import com.alibaba.druid.wall.WallProviderStatValue; - -/** - * @author ljw - * @author wenshao - */ -public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource { - - private final static Log LOG = LogFactory.getLog(DruidDataSource.class); - - private static final long serialVersionUID = 1L; - - // stats - private final AtomicLong recycleErrorCount = new AtomicLong(); - private long connectCount = 0L; - private long closeCount = 0L; - private final AtomicLong connectErrorCount = new AtomicLong(); - private long recycleCount = 0L; - private long removeAbandonedCount = 0L; - private long notEmptyWaitCount = 0L; - private long notEmptySignalCount = 0L; - private long notEmptyWaitNanos = 0L; - - private int activePeak = 0; - private long activePeakTime = 0; - private int poolingPeak = 0; - private long poolingPeakTime = 0; - - // store - private volatile DruidConnectionHolder[] connections; - private int poolingCount = 0; - private int activeCount = 0; - private long discardCount = 0; - private int notEmptyWaitThreadCount = 0; - private int notEmptyWaitThreadPeak = 0; - - // threads - private CreateConnectionThread createConnectionThread; - private DestroyConnectionThread destoryConnectionThread; - private LogStatsThread logStatsThread; - - private final CountDownLatch initedLatch = new CountDownLatch(2); - - private volatile boolean enable = true; - - private boolean resetStatEnable = true; - private final AtomicLong resetCount = new AtomicLong(); - - private String initStackTrace; - - private volatile boolean closed = false; - private long closeTimeMillis = -1L; - - protected JdbcDataSourceStat dataSourceStat; - - private boolean useGloalDataSourceStat = false; - - private boolean mbeanRegistered = false; - - public static ThreadLocal waitNanosLocal = new ThreadLocal(); - - public DruidDataSource(){ - this(false); - } - - public DruidDataSource(boolean fairLock){ - super(fairLock); - - configFromPropety(System.getProperties()); - } - - public void configFromPropety(Properties properties) { - { - Boolean value = getBoolean(properties, "druid.testWhileIdle"); - if (value != null) { - this.setTestWhileIdle(value); - } - } - { - Boolean value = getBoolean(properties, "druid.testOnBorrow"); - if (value != null) { - this.setTestOnBorrow(value); - } - } - { - String property = properties.getProperty("druid.validationQuery"); - if (property != null && property.length() > 0) { - this.setValidationQuery(property); - } - } - { - Boolean value = getBoolean(properties, "druid.useGloalDataSourceStat"); - if (value != null) { - this.setUseGloalDataSourceStat(value); - } - } - { - String property = properties.getProperty("druid.filters"); - - if (property != null && property.length() > 0) { - try { - this.setFilters(property); - } catch (SQLException e) { - LOG.error("setFilters error", e); - } - } - } - { - String property = properties.getProperty(Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS); - if (property != null && property.length() > 0) { - try { - long value = Long.parseLong(property); - this.setTimeBetweenLogStatsMillis(value); - } catch (NumberFormatException e) { - LOG.error("illegal property '" + Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS + "'", e); - } - } - } - { - String property = properties.getProperty(Constants.DRUID_STAT_SQL_MAX_SIZE); - if (property != null && property.length() > 0) { - try { - int value = Integer.parseInt(property); - if (dataSourceStat != null) { - dataSourceStat.setMaxSqlSize(value); - } - } catch (NumberFormatException e) { - LOG.error("illegal property '" + Constants.DRUID_STAT_SQL_MAX_SIZE + "'", e); - } - } - } - { - Boolean value = getBoolean(properties, "druid.clearFiltersEnable"); - if (value != null) { - this.setClearFiltersEnable(value); - } - } - { - Boolean value = getBoolean(properties, "druid.resetStatEnable"); - if (value != null) { - this.setResetStatEnable(value); - } - } - } - - public boolean isUseGloalDataSourceStat() { - return useGloalDataSourceStat; - } - - public void setUseGloalDataSourceStat(boolean useGloalDataSourceStat) { - this.useGloalDataSourceStat = useGloalDataSourceStat; - } - - public String getInitStackTrace() { - return initStackTrace; - } - - public boolean isResetStatEnable() { - return resetStatEnable; - } - - public void setResetStatEnable(boolean resetStatEnable) { - this.resetStatEnable = resetStatEnable; - if (dataSourceStat != null) { - dataSourceStat.setResetStatEnable(resetStatEnable); - } - } - - public long getDiscardCount() { - return discardCount; - } - - public void restart() { - lock.lock(); - try { - if (LOG.isInfoEnabled()) { - LOG.info("{dataSource-" + this.getID() + "} restart"); - } - - this.close(); - this.resetStat(); - this.inited = false; - this.enable = true; - this.closed = false; - } finally { - lock.unlock(); - } - } - - public void resetStat() { - if (!isResetStatEnable()) { - return; - } - - lock.lock(); - try { - connectCount = 0; - closeCount = 0; - discardCount = 0; - recycleCount = 0; - createCount.set(0); - destroyCount.set(0); - removeAbandonedCount = 0; - notEmptyWaitCount = 0; - notEmptySignalCount = 0L; - notEmptyWaitNanos = 0; - - activePeak = 0; - activePeakTime = 0; - poolingPeak = 0; - createTimespan = 0; - lastError = null; - lastErrorTimeMillis = 0; - lastCreateError = null; - lastCreateErrorTimeMillis = 0; - } finally { - lock.unlock(); - } - - connectErrorCount.set(0); - errorCount.set(0); - commitCount.set(0); - rollbackCount.set(0); - startTransactionCount.set(0); - cachedPreparedStatementHitCount.set(0); - closedPreparedStatementCount.set(0); - preparedStatementCount.set(0); - transactionHistogram.reset(); - cachedPreparedStatementDeleteCount.set(0); - recycleErrorCount.set(0); - - resetCount.incrementAndGet(); - } - - public long getResetCount() { - return this.resetCount.get(); - } - - public boolean isEnable() { - return enable; - } - - public void setEnable(boolean enable) { - lock.lock(); - try { - this.enable = enable; - if (!enable) { - notEmpty.signalAll(); - notEmptySignalCount++; - } - } finally { - lock.unlock(); - } - } - - public void setPoolPreparedStatements(boolean value) { - if (this.poolPreparedStatements == value) { - return; - } - - this.poolPreparedStatements = value; - - if (!inited) { - return; - } - - if (LOG.isInfoEnabled()) { - LOG.info("set poolPreparedStatements " + this.poolPreparedStatements + " -> " + value); - } - - if (!value) { - lock.lock(); - try { - - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder connection = connections[i]; - - for (PreparedStatementHolder holder : connection.getStatementPool().getMap().values()) { - closePreapredStatement(holder); - } - - connection.getStatementPool().getMap().clear(); - } - } finally { - lock.unlock(); - } - } - } - - public void setMaxActive(int maxActive) { - if (this.maxActive == maxActive) { - return; - } - - if (maxActive == 0) { - throw new IllegalArgumentException("maxActive can't not set zero"); - } - - if (!inited) { - this.maxActive = maxActive; - return; - } - - if (maxActive < this.minIdle) { - throw new IllegalArgumentException("maxActive less than minIdle, " + maxActive + " < " + this.minIdle); - } - - if (LOG.isInfoEnabled()) { - LOG.info("maxActive changed : " + this.maxActive + " -> " + maxActive); - } - - lock.lock(); - try { - int allCount = this.poolingCount + this.activeCount; - - if (maxActive > allCount) { - this.connections = Arrays.copyOf(this.connections, maxActive); - } else { - this.connections = Arrays.copyOf(this.connections, allCount); - } - - this.maxActive = maxActive; - } finally { - lock.unlock(); - } - } - - @SuppressWarnings("rawtypes") - public void setConnectProperties(Properties properties) { - if (properties == null) { - properties = new Properties(); - } - - boolean equals; - if (properties.size() == this.connectProperties.size()) { - equals = true; - for (Map.Entry entry : properties.entrySet()) { - Object value = this.connectProperties.get(entry.getKey()); - Object entryValue = entry.getValue(); - if (value == null && entryValue != null) { - equals = false; - break; - } - - if (!value.equals(entry.getValue())) { - equals = false; - break; - } - } - } else { - equals = false; - } - - if (!equals) { - if (inited && LOG.isInfoEnabled()) { - LOG.info("connectProperties changed : " + this.connectProperties + " -> " + properties); - } - - configFromPropety(properties); - - for (Filter filter : this.filters) { - filter.configFromProperties(properties); - } - - if (exceptionSorter != null) { - exceptionSorter.configFromProperties(properties); - } - - if (validConnectionChecker != null) { - validConnectionChecker.configFromProperties(properties); - } - - if (statLogger != null) { - statLogger.configFromProperties(properties); - } - } - - this.connectProperties = properties; - } - - public boolean isInited() { - return this.inited; - } - - public void init() throws SQLException { - if (inited) { - return; - } - - final ReentrantLock lock = this.lock; - try { - lock.lockInterruptibly(); - } catch (InterruptedException e) { - throw new SQLException("interrupt", e); - } - - boolean init = false; - try { - if (inited) { - return; - } - - init = true; - - initStackTrace = Utils.toString(Thread.currentThread().getStackTrace()); - - this.id = DruidDriver.createDataSourceId(); - if (this.id > 1) { - long delta = (this.id - 1) * 100000; - this.connectionIdSeed.addAndGet(delta); - this.statementIdSeed.addAndGet(delta); - this.resultSetIdSeed.addAndGet(delta); - this.transactionIdSeed.addAndGet(delta); - } - - if (this.dbType == null || this.dbType.length() == 0) { - this.dbType = JdbcUtils.getDbType(jdbcUrl, null); - } - - for (Filter filter : filters) { - filter.init(this); - } - - if (JdbcConstants.MYSQL.equals(this.dbType) || // - JdbcConstants.MARIADB.equals(this.dbType)) { - boolean cacheServerConfigurationSet = false; - if (this.connectProperties.containsKey("cacheServerConfiguration")) { - cacheServerConfigurationSet = true; - } else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) { - cacheServerConfigurationSet = true; - } - if (cacheServerConfigurationSet) { - this.connectProperties.put("cacheServerConfiguration", "true"); - } - } - - if (maxActive <= 0) { - throw new IllegalArgumentException("illegal maxActive " + maxActive); - } - - if (maxActive < minIdle) { - throw new IllegalArgumentException("illegal maxActive " + maxActive); - } - - if (getInitialSize() > maxActive) { - throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActieve " - + maxActive); - } - - if (timeBetweenLogStatsMillis > 0 && useGloalDataSourceStat) { - throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGloalDataSourceStat=true"); - } - - if (this.driverClass != null) { - this.driverClass = driverClass.trim(); - } - - if (this.jdbcUrl != null) { - this.jdbcUrl = this.jdbcUrl.trim(); - initFromWrapDriverUrl(); - } - - initFromSPIServiceLoader(); - - if (this.driver == null) { - if (this.driverClass == null || this.driverClass.isEmpty()) { - this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl); - } - - if (MockDriver.class.getName().equals(driverClass)) { - driver = MockDriver.instance; - } else { - driver = JdbcUtils.createDriver(driverClassLoader, driverClass); - } - } else { - if (this.driverClass == null) { - this.driverClass = driver.getClass().getName(); - } - } - - if (this.dbType == null || this.dbType.length() == 0) { - this.dbType = JdbcUtils.getDbType(jdbcUrl, driverClass.getClass().getName()); - } - - initCheck(); - - initExceptionSorter(); - initValidConnectionChecker(); - validationQueryCheck(); - - if (isUseGloalDataSourceStat()) { - dataSourceStat = JdbcDataSourceStat.getGlobal(); - if (dataSourceStat == null) { - dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbType); - JdbcDataSourceStat.setGlobal(dataSourceStat); - } - if (dataSourceStat.getDbType() == null) { - dataSourceStat.setDbType(this.getDbType()); - } - } else { - dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbType, this.connectProperties); - } - dataSourceStat.setResetStatEnable(this.resetStatEnable); - - connections = new DruidConnectionHolder[maxActive]; - - SQLException connectError = null; - - try { - // init connections - for (int i = 0, size = getInitialSize(); i < size; ++i) { - Connection conn = createPhysicalConnection(); - DruidConnectionHolder holder = new DruidConnectionHolder(this, conn); - connections[poolingCount++] = holder; - } - - if (poolingCount > 0) { - poolingPeak = poolingCount; - poolingPeakTime = System.currentTimeMillis(); - } - } catch (SQLException ex) { - LOG.error("init datasource error", ex); - connectError = ex; - } - - createAndLogThread(); - createAndStartCreatorThread(); - createAndStartDestroyThread(); - - initedLatch.await(); - - initedTime = new Date(); - registerMbean(); - - if (connectError != null && poolingCount == 0) { - throw connectError; - } - } catch (SQLException e) { - LOG.error("dataSource init error", e); - throw e; - } catch (InterruptedException e) { - throw new SQLException(e.getMessage(), e); - } finally { - inited = true; - lock.unlock(); - - if (init && LOG.isInfoEnabled()) { - LOG.info("{dataSource-" + this.getID() + "} inited"); - } - } - } - - private void createAndLogThread() { - if (this.timeBetweenLogStatsMillis <= 0) { - return; - } - - String threadName = "Druid-ConnectionPool-Log-" + System.identityHashCode(this); - logStatsThread = new LogStatsThread(threadName); - logStatsThread.start(); - - this.resetStatEnable = false; - } - - private void createAndStartDestroyThread() { - String threadName = "Druid-ConnectionPool-Destory-" + System.identityHashCode(this); - destoryConnectionThread = new DestroyConnectionThread(threadName); - destoryConnectionThread.start(); - } - - protected void createAndStartCreatorThread() { - String threadName = "Druid-ConnectionPool-Create-" + System.identityHashCode(this); - createConnectionThread = new CreateConnectionThread(threadName); - createConnectionThread.start(); - } - - /** - * load filters from SPI ServiceLoader - * - * @see ServiceLoader - */ - private void initFromSPIServiceLoader() { - - String property = System.getProperty("druid.load.spifilter.skip"); - if (property != null) { - return; - } - - ServiceLoader druidAutoFilterLoader = ServiceLoader.load(Filter.class); - - for (Filter autoFilter : druidAutoFilterLoader) { - AutoLoad autoLoad = autoFilter.getClass().getAnnotation(AutoLoad.class); - if (autoLoad != null && autoLoad.value()) { - if (LOG.isInfoEnabled()) { - LOG.info("load filter from spi :" + autoFilter.getClass().getName()); - } - addFilter(autoFilter); - } - } - } - - private void initFromWrapDriverUrl() throws SQLException { - if (!jdbcUrl.startsWith(DruidDriver.DEFAULT_PREFIX)) { - return; - } - - DataSourceProxyConfig config = DruidDriver.parseConfig(jdbcUrl, null); - this.driverClass = config.getRawDriverClassName(); - - LOG.error("error url : '" + jdbcUrl + "', it should be : '" + config.getRawUrl() + "'"); - - this.jdbcUrl = config.getRawUrl(); - if (this.name == null) { - this.name = config.getName(); - } - - for (Filter filter : config.getFilters()) { - addFilter(filter); - } - } - - /** - * 会去重复 - * - * @param filter - */ - private void addFilter(Filter filter) { - boolean exists = false; - for (Filter initedFilter : this.filters) { - if (initedFilter.getClass() == filter.getClass()) { - exists = true; - break; - } - } - - if (!exists) { - filter.init(this); - this.filters.add(filter); - } - - } - - private void validationQueryCheck() { - if (!(isTestOnBorrow() || isTestOnReturn() || isTestWhileIdle())) { - return; - } - - if (this.validConnectionChecker != null) { - return; - } - - if (this.getValidationQuery() != null && this.getValidationQuery().length() > 0) { - return; - } - - String errorMessage = ""; - - if (isTestOnBorrow()) { - errorMessage += "testOnBorrow is true, "; - } - - if (isTestOnReturn()) { - errorMessage += "testOnReturn is true, "; - } - - if (isTestWhileIdle()) { - errorMessage += "testWhileIdle is true, "; - } - - LOG.error(errorMessage + "validationQuery not set"); - } - - protected void initCheck() throws SQLException { - if (JdbcUtils.ORACLE.equals(this.dbType)) { - isOracle = true; - - if (driver.getMajorVersion() < 10) { - throw new SQLException("not support oracle driver " + driver.getMajorVersion() + "." - + driver.getMinorVersion()); - } - - if (driver.getMajorVersion() == 10 && isUseOracleImplicitCache()) { - this.getConnectProperties().setProperty("oracle.jdbc.FreeMemoryOnEnterImplicitCache", "true"); - } - - oracleValidationQueryCheck(); - } else if (JdbcUtils.DB2.equals(dbType)) { - db2ValidationQueryCheck(); - } - } - - private void oracleValidationQueryCheck() { - if (validationQuery == null) { - return; - } - if (validationQuery.length() == 0) { - return; - } - - SQLStatementParser sqlStmtParser = SQLParserUtils.createSQLStatementParser(validationQuery, this.dbType); - List stmtList = sqlStmtParser.parseStatementList(); - - if (stmtList.size() != 1) { - return; - } - - SQLStatement stmt = stmtList.get(0); - if (!(stmt instanceof SQLSelectStatement)) { - return; - } - - SQLSelectQuery query = ((SQLSelectStatement) stmt).getSelect().getQuery(); - if (query instanceof SQLSelectQueryBlock) { - if (((SQLSelectQueryBlock) query).getFrom() == null) { - LOG.error("invalid oracle validationQuery. " + validationQuery + ", may should be : " + validationQuery - + " FROM DUAL"); - } - } - } - - private void db2ValidationQueryCheck() { - if (validationQuery == null) { - return; - } - if (validationQuery.length() == 0) { - return; - } - - SQLStatementParser sqlStmtParser = SQLParserUtils.createSQLStatementParser(validationQuery, this.dbType); - List stmtList = sqlStmtParser.parseStatementList(); - - if (stmtList.size() != 1) { - return; - } - - SQLStatement stmt = stmtList.get(0); - if (!(stmt instanceof SQLSelectStatement)) { - return; - } - - SQLSelectQuery query = ((SQLSelectStatement) stmt).getSelect().getQuery(); - if (query instanceof SQLSelectQueryBlock) { - if (((SQLSelectQueryBlock) query).getFrom() == null) { - LOG.error("invalid db2 validationQuery. " + validationQuery + ", may should be : " + validationQuery - + " FROM SYSDUMMY"); - } - } - } - - private void initValidConnectionChecker() { - String realDriverClassName = driver.getClass().getName(); - if (realDriverClassName.equals("com.mysql.jdbc.Driver")) { - this.validConnectionChecker = new MySqlValidConnectionChecker(); - } else if (realDriverClassName.equals("oracle.jdbc.driver.OracleDriver")) { - this.validConnectionChecker = new OracleValidConnectionChecker(); - } else if (realDriverClassName.equals("com.microsoft.jdbc.sqlserver.SQLServerDriver")) { - this.validConnectionChecker = new MSSQLValidConnectionChecker(); - } - } - - private void initExceptionSorter() { - String realDriverClassName = driver.getClass().getName(); - if (realDriverClassName.equals(JdbcConstants.MYSQL_DRIVER)) { - this.exceptionSorter = new MySqlExceptionSorter(); - } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER)) { - this.exceptionSorter = new OracleExceptionSorter(); - } else if (realDriverClassName.equals("com.informix.jdbc.IfxDriver")) { - this.exceptionSorter = new InformixExceptionSorter(); - - } else if (realDriverClassName.equals("com.sybase.jdbc2.jdbc.SybDriver")) { - this.exceptionSorter = new SybaseExceptionSorter(); - - } else if (realDriverClassName.equals("com.alibaba.druid.mock.MockDriver")) { - this.exceptionSorter = new MockExceptionSorter(); - } else if (realDriverClassName.contains("DB2")) { - this.exceptionSorter = new DB2ExceptionSorter(); - } - } - - @Override - public DruidPooledConnection getConnection() throws SQLException { - return getConnection(maxWait); - } - - public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException { - init(); - - if (filters.size() > 0) { - FilterChainImpl filterChain = new FilterChainImpl(this); - return filterChain.dataSource_connect(this, maxWaitMillis); - } else { - return getConnectionDirect(maxWaitMillis); - } - } - - @Override - public PooledConnection getPooledConnection() throws SQLException { - return getConnection(maxWait); - } - - @Override - public PooledConnection getPooledConnection(String user, String password) throws SQLException { - throw new UnsupportedOperationException("Not supported by DruidDataSource"); - } - - public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException { - for (;;) { - DruidPooledConnection poolableConnection = getConnectionInternal(maxWaitMillis); - - if (isTestOnBorrow()) { - boolean validate = testConnectionInternal(poolableConnection.getConnection()); - if (!validate) { - if (LOG.isDebugEnabled()) { - LOG.debug("skip not validate connection."); - } - - Connection realConnection = poolableConnection.getConnection(); - discardConnection(realConnection); - continue; - } - } else { - Connection realConnection = poolableConnection.getConnection(); - if (realConnection.isClosed()) { - discardConnection(null); // 传入null,避免重复关闭 - continue; - } - - if (isTestWhileIdle()) { - final long currentTimeMillis = System.currentTimeMillis(); - final long lastActiveTimeMillis = poolableConnection.getConnectionHolder().getLastActiveTimeMillis(); - final long idleMillis = currentTimeMillis - lastActiveTimeMillis; - long timeBetweenEvictionRunsMillis = this.getTimeBetweenEvictionRunsMillis(); - if (timeBetweenEvictionRunsMillis <= 0) { - timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; - } - - if (idleMillis >= timeBetweenEvictionRunsMillis) { - boolean validate = testConnectionInternal(poolableConnection.getConnection()); - if (!validate) { - if (LOG.isDebugEnabled()) { - LOG.debug("skip not validate connection."); - } - - discardConnection(realConnection); - continue; - } - } - } - } - - if (isRemoveAbandoned()) { - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - poolableConnection.setConnectStackTrace(stackTrace); - poolableConnection.setConnectedTimeNano(); - poolableConnection.setTraceEnable(true); - - synchronized (activeConnections) { - activeConnections.put(poolableConnection, PRESENT); - } - } - - if (!this.isDefaultAutoCommit()) { - poolableConnection.setAutoCommit(false); - } - - return poolableConnection; - } - } - - /** - * 抛弃连接,不进行回收,而是抛弃 - * - * @param realConnection - * @throws SQLException - */ - public void discardConnection(Connection realConnection) { - JdbcUtils.close(realConnection); - - try { - lock.lockInterruptibly(); - } catch (InterruptedException e) { - LOG.error("interrupt"); - return; - } - try { - activeCount--; - discardCount++; - - if (activeCount <= 0) { - empty.signal(); - } - } finally { - lock.unlock(); - } - } - - private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException { - if (closed) { - connectErrorCount.incrementAndGet(); - throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis)); - } - - if (!enable) { - connectErrorCount.incrementAndGet(); - throw new DataSourceDisableException(); - } - - final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait); - final int maxWaitThreadCount = getMaxWaitThreadCount(); - - DruidConnectionHolder holder; - try { - lock.lockInterruptibly(); - } catch (InterruptedException e) { - connectErrorCount.incrementAndGet(); - throw new SQLException("interrupt", e); - } - - try { - if (maxWaitThreadCount > 0) { - if (notEmptyWaitThreadCount >= maxWaitThreadCount) { - connectErrorCount.incrementAndGet(); - throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count " - + lock.getQueueLength()); - } - } - - connectCount++; - - if (maxWait > 0) { - holder = pollLast(nanos); - } else { - holder = takeLast(); - } - - if (holder != null) { - activeCount++; - if (activeCount > activePeak) { - activePeak = activeCount; - activePeakTime = System.currentTimeMillis(); - } - } - } catch (InterruptedException e) { - connectErrorCount.incrementAndGet(); - throw new SQLException(e.getMessage(), e); - } catch (SQLException e) { - connectErrorCount.incrementAndGet(); - throw e; - } finally { - lock.unlock(); - } - - if (holder == null) { - long waitNanos = waitNanosLocal.get(); - - StringBuilder buf = new StringBuilder(); - buf.append("wait millis ")// - .append(waitNanos / (1000 * 1000))// - .append(", active " + activeCount)// - ; - - List sqlList = this.getDataSourceStat().getRuningSqlList(); - for (int i = 0; i < sqlList.size(); ++i) { - if (i != 0) { - buf.append('\n'); - } else { - buf.append(", "); - } - JdbcSqlStatValue sql = sqlList.get(i); - buf.append("runningSqlCount "); - buf.append(sql.getRunningCount()); - buf.append(" : "); - buf.append(sql.getSql()); - } - - String errorMessage = buf.toString(); - - if (this.createError != null) { - throw new GetConnectionTimeoutException(errorMessage, createError); - } else { - throw new GetConnectionTimeoutException(errorMessage); - } - } - - holder.incrementUseCount(); - - DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder); - return poolalbeConnection; - } - - public void handleConnectionException(DruidPooledConnection pooledConnection, Throwable t) throws SQLException { - final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); - - errorCount.incrementAndGet(); - lastError = t; - lastErrorTimeMillis = System.currentTimeMillis(); - - if (t instanceof SQLException) { - SQLException sqlEx = (SQLException) t; - - // broadcastConnectionError - ConnectionEvent event = new ConnectionEvent(pooledConnection, sqlEx); - for (ConnectionEventListener eventListener : holder.getConnectionEventListeners()) { - eventListener.connectionErrorOccurred(event); - } - - // exceptionSorter.isExceptionFatal - if (exceptionSorter != null && exceptionSorter.isExceptionFatal(sqlEx)) { - if (pooledConnection.isTraceEnable()) { - synchronized (activeConnections) { - if (pooledConnection.isTraceEnable()) { - activeConnections.remove(pooledConnection); - pooledConnection.setTraceEnable(false); - } - } - } - this.discardConnection(holder.getConnection()); - holder.setDiscard(true); - pooledConnection.disable(t); - - LOG.error("discard connection", sqlEx); - } - - throw sqlEx; - } else { - throw new SQLException("Error", t); - } - } - - /** - * 回收连接 - */ - protected void recycle(DruidPooledConnection pooledConnection) throws SQLException { - final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); - - if (holder == null) { - LOG.warn("connectionHolder is null"); - return; - } - - if (pooledConnection.getOwnerThread() != Thread.currentThread()) { - LOG.warn("get/close not same thread"); - } - - final Connection physicalConnection = holder.getConnection(); - - if (pooledConnection.isTraceEnable()) { - synchronized (activeConnections) { - if (pooledConnection.isTraceEnable()) { - Object oldInfo = activeConnections.remove(pooledConnection); - if (oldInfo == null) { - if (LOG.isWarnEnabled()) { - LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size()); - } - } - pooledConnection.setTraceEnable(false); - } - } - } - - final boolean isAutoCommit = holder.isUnderlyingAutoCommit(); - final boolean isReadOnly = holder.isUnderlyingReadOnly(); - final boolean testOnReturn = this.isTestOnReturn(); - - try { - // check need to rollback? - if ((!isAutoCommit) && (!isReadOnly)) { - pooledConnection.rollback(); - } - - // reset holder, restore default settings, clear warnings - holder.reset(); - - if (testOnReturn) { - boolean validate = testConnectionInternal(physicalConnection); - if (!validate) { - JdbcUtils.close(physicalConnection); - - destroyCount.incrementAndGet(); - - lock.lockInterruptibly(); - try { - activeCount--; - closeCount++; - } finally { - lock.unlock(); - } - return; - } - } - - if (!enable) { - discardConnection(holder.getConnection()); - return; - } - - final long lastActiveTimeMillis = System.currentTimeMillis(); - lock.lockInterruptibly(); - try { - activeCount--; - closeCount++; - - putLast(holder, lastActiveTimeMillis); - recycleCount++; - } finally { - lock.unlock(); - } - } catch (Throwable e) { - holder.clearStatementCache(); - - if (!holder.isDiscard()) { - this.discardConnection(physicalConnection); - holder.setDiscard(true); - } - - LOG.error("recyle error", e); - recycleErrorCount.incrementAndGet(); - } - } - - public long getRecycleErrorCount() { - return recycleErrorCount.get(); - } - - public void clearStatementCache() throws SQLException { - lock.lock(); - try { - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder conn = connections[i]; - conn.getStatementPool().clear(); - } - } finally { - lock.unlock(); - } - } - - /** - * close datasource - */ - public void close() { - lock.lock(); - try { - if (this.closed) { - return; - } - - if (!this.inited) { - return; - } - - if (logStatsThread != null) { - logStatsThread.interrupt(); - } - - if (createConnectionThread != null) { - createConnectionThread.interrupt(); - } - - if (destoryConnectionThread != null) { - destoryConnectionThread.interrupt(); - } - - for (int i = 0; i < poolingCount; ++i) { - try { - DruidConnectionHolder connHolder = connections[i]; - - for (PreparedStatementHolder stmtHolder : connHolder.getStatementPool().getMap().values()) { - connHolder.getStatementPool().closeRemovedStatement(stmtHolder); - } - connHolder.getStatementPool().getMap().clear(); - - Connection physicalConnection = connHolder.getConnection(); - physicalConnection.close(); - connections[i] = null; - destroyCount.incrementAndGet(); - } catch (Exception ex) { - LOG.warn("close connection error", ex); - } - } - poolingCount = 0; - unregisterMbean(); - - enable = false; - notEmpty.signalAll(); - notEmptySignalCount++; - - this.closed = true; - this.closeTimeMillis = System.currentTimeMillis(); - - for (Filter filter : filters) { - filter.destory(); - } - } finally { - lock.unlock(); - } - - if (LOG.isInfoEnabled()) { - LOG.info("{dataSource-" + this.getID() + "} closed"); - } - } - - public void registerMbean() { - if (!mbeanRegistered) { - AccessController.doPrivileged(new PrivilegedAction() { - - @Override - public Object run() { - ObjectName objectName = DruidDataSourceStatManager.addDataSource(DruidDataSource.this, - DruidDataSource.this.name); - DruidDataSource.this.setObjectName(objectName); - DruidDataSource.this.mbeanRegistered = true; - return null; - } - }); - } - } - - public void unregisterMbean() { - if (mbeanRegistered) { - AccessController.doPrivileged(new PrivilegedAction() { - - @Override - public Object run() { - DruidDataSourceStatManager.removeDataSource(DruidDataSource.this); - DruidDataSource.this.mbeanRegistered = false; - return null; - } - }); - } - } - - public boolean isMbeanRegistered() { - return mbeanRegistered; - } - - void putLast(DruidConnectionHolder e, long lastActiveTimeMillis) { - e.setLastActiveTimeMillis(lastActiveTimeMillis); - connections[poolingCount++] = e; - - if (poolingCount > poolingPeak) { - poolingPeak = poolingCount; - poolingPeakTime = lastActiveTimeMillis; - } - - notEmpty.signal(); - notEmptySignalCount++; - } - - DruidConnectionHolder takeLast() throws InterruptedException, SQLException { - try { - while (poolingCount == 0) { - empty.signal(); // send signal to CreateThread create connection - notEmptyWaitThreadCount++; - if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) { - notEmptyWaitThreadPeak = notEmptyWaitThreadCount; - } - try { - notEmpty.await(); // signal by recycle or creator - } finally { - notEmptyWaitThreadCount--; - } - notEmptyWaitCount++; - - if (!enable) { - connectErrorCount.incrementAndGet(); - throw new DataSourceDisableException(); - } - } - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - notEmptySignalCount++; - throw ie; - } - - poolingCount--; - DruidConnectionHolder last = connections[poolingCount]; - connections[poolingCount] = null; - - return last; - } - - DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException { - long estimate = nanos; - - for (;;) { - if (poolingCount == 0) { - empty.signal(); // send signal to CreateThread create connection - - if (estimate <= 0) { - waitNanosLocal.set(nanos - estimate); - return null; - } - - notEmptyWaitThreadCount++; - if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) { - notEmptyWaitThreadPeak = notEmptyWaitThreadCount; - } - - try { - long startEstimate = estimate; - estimate = notEmpty.awaitNanos(estimate); // signal by - // recycle or - // creator - notEmptyWaitCount++; - notEmptyWaitNanos += (startEstimate - estimate); - - if (!enable) { - connectErrorCount.incrementAndGet(); - throw new DataSourceDisableException(); - } - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - notEmptySignalCount++; - throw ie; - } finally { - notEmptyWaitThreadCount--; - } - - if (poolingCount == 0) { - if (estimate > 0) { - continue; - } - - waitNanosLocal.set(nanos - estimate); - return null; - } - } - - poolingCount--; - DruidConnectionHolder last = connections[poolingCount]; - connections[poolingCount] = null; - - return last; - } - } - - @Override - public Connection getConnection(String username, String password) throws SQLException { - if (!StringUtils.equals(username, this.username)) { - throw new UnsupportedOperationException("Not supported by DruidDataSource"); - } - - if (!StringUtils.equals(password, this.password)) { - throw new UnsupportedOperationException("Not supported by DruidDataSource"); - } - - return getConnection(); - } - - public long getCreateCount() { - return createCount.get(); - } - - public long getDestroyCount() { - return destroyCount.get(); - } - - public long getConnectCount() { - lock.lock(); - try { - return connectCount; - } finally { - lock.unlock(); - } - } - - public long getCloseCount() { - return closeCount; - } - - public long getConnectErrorCount() { - return connectErrorCount.get(); - } - - @Override - public int getPoolingCount() { - lock.lock(); - try { - return poolingCount; - } finally { - lock.unlock(); - } - } - - public int getPoolingPeak() { - lock.lock(); - try { - return poolingPeak; - } finally { - lock.unlock(); - } - } - - public Date getPoolingPeakTime() { - if (poolingPeakTime <= 0) { - return null; - } - - return new Date(poolingPeakTime); - } - - public long getRecycleCount() { - return recycleCount; - } - - public int getActiveCount() { - lock.lock(); - try { - return activeCount; - } finally { - lock.unlock(); - } - } - - public void logStats() { - final DruidDataSourceStatLogger statLogger = this.statLogger; - if (statLogger == null) { - return; - } - - DruidDataSourceStatValue statValue = getStatValueAndReset(); - - statLogger.log(statValue); - } - - public DruidDataSourceStatValue getStatValueAndReset() { - DruidDataSourceStatValue value = new DruidDataSourceStatValue(); - - lock.lock(); - try { - value.setPoolingCount(this.poolingCount); - value.setPoolingPeak(this.poolingPeak); - value.setPoolingPeakTime(this.poolingPeakTime); - - value.setActiveCount(this.activeCount); - value.setActivePeak(this.activePeak); - value.setActivePeakTime(this.activePeakTime); - - value.setConnectCount(this.connectCount); - value.setCloseCount(this.closeCount); - value.setWaitThreadCount(lock.getWaitQueueLength(notEmpty)); - value.setNotEmptyWaitCount(this.notEmptyWaitCount); - value.setNotEmptyWaitNanos(this.notEmptyWaitNanos); - - // reset - this.poolingPeak = 0; - this.poolingPeakTime = 0; - this.activePeak = 0; - this.activePeakTime = 0; - this.connectCount = 0; - this.closeCount = 0; - - this.notEmptyWaitCount = 0; - this.notEmptyWaitNanos = 0; - } finally { - lock.unlock(); - } - - value.setName(this.getName()); - value.setDbType(this.getDbType()); - value.setDriverClassName(this.getDriverClassName()); - - value.setUrl(this.getUrl()); - value.setUserName(this.getUsername()); - value.setFilterClassNames(this.getFilterClassNames()); - - value.setInitialSize(this.getInitialSize()); - value.setMinIdle(this.getMinIdle()); - value.setMaxActive(this.getMaxActive()); - - value.setQueryTimeout(this.getQueryTimeout()); - value.setTransactionQueryTimeout(this.getTransactionQueryTimeout()); - value.setLoginTimeout(this.getLoginTimeout()); - value.setValidConnectionCheckerClassName(this.getValidConnectionCheckerClassName()); - value.setExceptionSorterClassName(this.getExceptionSorterClassName()); - - value.setTestOnBorrow(this.isTestOnBorrow()); - value.setTestOnReturn(this.isTestOnReturn()); - value.setTestWhileIdle(this.isTestWhileIdle()); - - value.setDefaultAutoCommit(this.isDefaultAutoCommit()); - value.setDefaultReadOnly(this.getDefaultReadOnly()); - value.setDefaultTransactionIsolation(this.getDefaultTransactionIsolation()); - - value.setLogicConnectErrorCount(connectErrorCount.getAndSet(0)); - - value.setPhysicalConnectCount(createCount.getAndSet(0)); - value.setPhysicalCloseCount(destroyCount.getAndSet(0)); - value.setPhysicalConnectErrorCount(createErrorCount.getAndSet(0)); - - value.setExecuteCount(this.executeCount.getAndSet(0)); - value.setErrorCount(errorCount.getAndSet(0)); - value.setCommitCount(commitCount.getAndSet(0)); - value.setRollbackCount(rollbackCount.getAndSet(0)); - - value.setPstmtCacheHitCount(cachedPreparedStatementHitCount.getAndSet(0)); - value.setPstmtCacheMissCount(cachedPreparedStatementMissCount.getAndSet(0)); - - value.setStartTransactionCount(startTransactionCount.getAndSet(0)); - value.setTransactionHistogram(this.getTransactionHistogram().toArrayAndReset()); - - value.setConnectionHoldTimeHistogram(this.getDataSourceStat().getConnectionHoldHistogram().toArrayAndReset()); - value.removeAbandoned = this.isRemoveAbandoned(); - value.setClobOpenCount(this.getDataSourceStat().getClobOpenCountAndReset()); - value.setBlobOpenCount(this.getDataSourceStat().getBlobOpenCountAndReset()); - - value.setSqlSkipCount(this.getDataSourceStat().getSkipSqlCountAndReset()); - value.setSqlList(this.getDataSourceStat().getSqlStatMapAndReset()); - - return value; - } - - public long getRemoveAbandonedCount() { - return removeAbandonedCount; - } - - public class CreateConnectionThread extends Thread { - - public CreateConnectionThread(String name){ - super(name); - this.setDaemon(true); - } - - public void run() { - initedLatch.countDown(); - - int errorCount = 0; - for (;;) { - // addLast - try { - lock.lockInterruptibly(); - } catch (InterruptedException e2) { - break; - } - - try { - // 必须存在线程等待,才创建连接 - if (poolingCount >= notEmptyWaitThreadCount) { - empty.await(); - } - - // 防止创建超过maxActive数量的连接 - if (activeCount + poolingCount >= maxActive) { - empty.await(); - continue; - } - - } catch (InterruptedException e) { - lastCreateError = e; - lastErrorTimeMillis = System.currentTimeMillis(); - break; - } finally { - lock.unlock(); - } - - Connection connection = null; - - try { - connection = createPhysicalConnection(); - } catch (SQLException e) { - LOG.error("create connection error", e); - - errorCount++; - - if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) { - if (breakAfterAcquireFailure) { - break; - } - - try { - Thread.sleep(timeBetweenConnectErrorMillis); - } catch (InterruptedException interruptEx) { - break; - } - } - } catch (RuntimeException e) { - LOG.error("create connection error", e); - continue; - } catch (Error e) { - LOG.error("create connection error", e); - break; - } - - if (connection == null) { - continue; - } - - DruidConnectionHolder holder = null; - try { - holder = new DruidConnectionHolder(DruidDataSource.this, connection); - } catch (SQLException ex) { - LOG.error("create connection holder error", ex); - break; - } - - lock.lock(); - try { - connections[poolingCount++] = holder; - - if (poolingCount > poolingPeak) { - poolingPeak = poolingCount; - poolingPeakTime = System.currentTimeMillis(); - } - - errorCount = 0; // reset errorCount - - notEmpty.signal(); - notEmptySignalCount++; - } finally { - lock.unlock(); - } - } - } - } - - public class DestroyConnectionThread extends Thread { - - public DestroyConnectionThread(String name){ - super(name); - this.setDaemon(true); - } - - public void run() { - initedLatch.countDown(); - - for (;;) { - // 从前面开始删除 - try { - if (closed) { - break; - } - - if (timeBetweenEvictionRunsMillis > 0) { - Thread.sleep(timeBetweenEvictionRunsMillis); - } else { - Thread.sleep(1000); // - } - - if (Thread.interrupted()) { - break; - } - - shrink(true); - - if (isRemoveAbandoned()) { - removeAbandoned(); - } - } catch (InterruptedException e) { - break; - } - } - } - - } - - public class LogStatsThread extends Thread { - - public LogStatsThread(String name){ - super(name); - this.setDaemon(true); - } - - public void run() { - try { - for (;;) { - try { - logStats(); - } catch (Exception e) { - LOG.error("logStats error", e); - } - - Thread.sleep(timeBetweenLogStatsMillis); - } - } catch (InterruptedException e) { - // skip - } - } - } - - public int removeAbandoned() { - int removeCount = 0; - - long currrentNanos = System.nanoTime(); - - List abandonedList = new ArrayList(); - - synchronized (activeConnections) { - Iterator iter = activeConnections.keySet().iterator(); - - for (; iter.hasNext();) { - DruidPooledConnection pooledConnection = iter.next(); - - if (pooledConnection.isRunning()) { - continue; - } - - long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000); - - if (timeMillis >= removeAbandonedTimeoutMillis) { - iter.remove(); - pooledConnection.setTraceEnable(false); - abandonedList.add(pooledConnection); - } - } - } - - if (abandonedList.size() > 0) { - for (DruidPooledConnection pooledConnection : abandonedList) { - JdbcUtils.close(pooledConnection); - pooledConnection.abandond(); - removeAbandonedCount++; - removeCount++; - - if (isLogAbandoned()) { - StringBuilder buf = new StringBuilder(); - buf.append("abandon connection, open stackTrace\n"); - - StackTraceElement[] trace = pooledConnection.getConnectStackTrace(); - for (int i = 0; i < trace.length; i++) { - buf.append("\tat "); - buf.append(trace[i].toString()); - buf.append("\n"); - } - - LOG.error(buf.toString()); - } - } - } - - return removeCount; - } - - /** Instance key */ - protected String instanceKey = null; - - public Reference getReference() throws NamingException { - final String className = getClass().getName(); - final String factoryName = className + "Factory"; // XXX: not robust - Reference ref = new Reference(className, factoryName, null); - ref.add(new StringRefAddr("instanceKey", instanceKey)); - ref.add(new StringRefAddr("url", this.getUrl())); - ref.add(new StringRefAddr("username", this.getUsername())); - ref.add(new StringRefAddr("password", this.getPassword())); - // TODO ADD OTHER PROPERTIES - return ref; - } - - @Override - public List getFilterClassNames() { - List names = new ArrayList(); - for (Filter filter : filters) { - names.add(filter.getClass().getName()); - } - return names; - } - - public int getRawDriverMajorVersion() { - int version = -1; - if (this.driver != null) { - version = driver.getMajorVersion(); - } - return version; - } - - public int getRawDriverMinorVersion() { - int version = -1; - if (this.driver != null) { - version = driver.getMinorVersion(); - } - return version; - } - - public String getProperties() { - Properties properties = new Properties(); - properties.putAll(connectProperties); - if (properties.containsKey("password")) { - properties.put("password", "******"); - } - return properties.toString(); - } - - @Override - public void shrink() { - shrink(false); - } - - public void shrink(boolean checkTime) { - final List evictList = new ArrayList(); - try { - lock.lockInterruptibly(); - } catch (InterruptedException e) { - return; - } - - try { - final int checkCount = poolingCount - minIdle; - final long currentTimeMillis = System.currentTimeMillis(); - for (int i = 0; i < checkCount; ++i) { - DruidConnectionHolder connection = connections[i]; - - if (checkTime) { - long idleMillis = currentTimeMillis - connection.getLastActiveTimeMillis(); - if (idleMillis >= minEvictableIdleTimeMillis) { - evictList.add(connection); - } else { - break; - } - } else { - evictList.add(connection); - } - } - - int removeCount = evictList.size(); - if (removeCount > 0) { - System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount); - Arrays.fill(connections, poolingCount - removeCount, poolingCount, null); - poolingCount -= removeCount; - } - } finally { - lock.unlock(); - } - - for (DruidConnectionHolder item : evictList) { - Connection connection = item.getConnection(); - JdbcUtils.close(connection); - destroyCount.incrementAndGet(); - } - } - - public int getWaitThreadCount() { - lock.lock(); - try { - return lock.getWaitQueueLength(notEmpty); - } finally { - lock.unlock(); - } - } - - public long getNotEmptyWaitCount() { - return notEmptyWaitCount; - } - - public int getNotEmptyWaitThreadCount() { - lock.lock(); - try { - return notEmptyWaitThreadCount; - } finally { - lock.unlock(); - } - } - - public int getNotEmptyWaitThreadPeak() { - lock.lock(); - try { - return notEmptyWaitThreadPeak; - } finally { - lock.unlock(); - } - } - - public long getNotEmptySignalCount() { - return notEmptySignalCount; - } - - public long getNotEmptyWaitMillis() { - return notEmptyWaitNanos / (1000 * 1000); - } - - public long getNotEmptyWaitNanos() { - return notEmptyWaitNanos; - } - - public int getLockQueueLength() { - return lock.getQueueLength(); - } - - public int getActivePeak() { - return activePeak; - } - - public Date getActivePeakTime() { - if (activePeakTime <= 0) { - return null; - } - - return new Date(activePeakTime); - } - - public String dump() { - lock.lock(); - try { - return this.toString(); - } finally { - lock.unlock(); - } - } - - public long getErrorCount() { - return this.errorCount.get(); - } - - public String toString() { - StringBuilder buf = new StringBuilder(); - - buf.append("{"); - - buf.append("\n\tCreateTime:\""); - buf.append(Utils.toString(getCreatedTime())); - buf.append("\""); - - buf.append(",\n\tActiveCount:"); - buf.append(getActiveCount()); - - buf.append(",\n\tPoolingCount:"); - buf.append(getPoolingCount()); - - buf.append(",\n\tCreateCount:"); - buf.append(getCreateCount()); - - buf.append(",\n\tDestroyCount:"); - buf.append(getDestroyCount()); - - buf.append(",\n\tCloseCount:"); - buf.append(getCloseCount()); - - buf.append(",\n\tConnectCount:"); - buf.append(getConnectCount()); - - buf.append(",\n\tConnections:["); - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder conn = connections[i]; - if (conn != null) { - if (i != 0) { - buf.append(","); - } - buf.append("\n\t\t"); - buf.append(conn.toString()); - } - } - buf.append("\n\t]"); - - buf.append("\n}"); - - if (this.isPoolPreparedStatements()) { - buf.append("\n\n["); - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder conn = connections[i]; - if (conn != null) { - if (i != 0) { - buf.append(","); - } - buf.append("\n\t{\n\tID:"); - buf.append(System.identityHashCode(conn.getConnection())); - PreparedStatementPool pool = conn.getStatementPool(); - - buf.append(", \n\tpoolStatements:["); - - int entryIndex = 0; - try { - for (Map.Entry entry : pool.getMap().entrySet()) { - if (entryIndex != 0) { - buf.append(","); - } - buf.append("\n\t\t{hitCount:"); - buf.append(entry.getValue().getHitCount()); - buf.append(",sql:\""); - buf.append(entry.getKey().getSql()); - buf.append("\""); - buf.append("\t}"); - - entryIndex++; - } - } catch (ConcurrentModificationException e) { - // skip .. - } - - buf.append("\n\t\t]"); - - buf.append("\n\t}"); - } - } - buf.append("\n]"); - } - - return buf.toString(); - } - - public List> getPoolingConnectionInfo() { - List> list = new ArrayList>(); - lock.lock(); - try { - for (int i = 0; i < poolingCount; ++i) { - DruidConnectionHolder connHolder = connections[i]; - Connection conn = connHolder.getConnection(); - - Map map = new LinkedHashMap(); - map.put("id", System.identityHashCode(conn)); - map.put("useCount", connHolder.getUseCount()); - if (connHolder.getLastActiveTimeMillis() > 0) { - map.put("lastActiveTime", new Date(connHolder.getLastActiveTimeMillis())); - } - map.put("connectTime", new Date(connHolder.getTimeMillis())); - map.put("holdability", connHolder.getUnderlyingHoldability()); - map.put("transactionIsolation", connHolder.getUnderlyingTransactionIsolation()); - map.put("autoCommit", connHolder.isUnderlyingAutoCommit()); - map.put("readoOnly", connHolder.isUnderlyingReadOnly()); - - if (connHolder.isPoolPreparedStatements()) { - List> stmtCache = new ArrayList>(); - PreparedStatementPool stmtPool = connHolder.getStatementPool(); - for (PreparedStatementHolder stmtHolder : stmtPool.getMap().values()) { - Map stmtInfo = new LinkedHashMap(); - - stmtInfo.put("sql", stmtHolder.getKey().getSql()); - stmtInfo.put("defaultRowPretch", stmtHolder.getDefaultRowPretch()); - stmtInfo.put("rowPrefetch", stmtHolder.getRowPrefetch()); - stmtInfo.put("hitCount", stmtHolder.getHitCount()); - - stmtCache.add(stmtInfo); - } - - map.put("pscache", stmtCache); - } - - list.add(map); - } - } finally { - lock.unlock(); - } - return list; - } - - public void logTransaction(TransactionInfo info) { - long transactionMillis = info.getEndTimeMillis() - info.getStartTimeMillis(); - if (transactionThresholdMillis > 0 && transactionMillis > transactionThresholdMillis) { - StringBuilder buf = new StringBuilder(); - buf.append("long time transaction, take "); - buf.append(transactionMillis); - buf.append(" ms : "); - for (String sql : info.getSqlList()) { - buf.append(sql); - buf.append(";"); - } - LOG.error(buf.toString(), new TransactionTimeoutException()); - } - } - - @Override - public String getVersion() { - return VERSION.getVersionNumber(); - } - - @Override - public JdbcDataSourceStat getDataSourceStat() { - return dataSourceStat; - } - - public Object clone() throws CloneNotSupportedException { - return cloneDruidDataSource(); - } - - public DruidDataSource cloneDruidDataSource() { - DruidDataSource x = new DruidDataSource(); - - cloneTo(x); - - return x; - } - - public Map getStatDataForMBean() { - try { - Map map = new HashMap(); - - // 0 - 4 - map.put("Name", this.getName()); - map.put("URL", this.getUrl()); - map.put("CreateCount", this.getCreateCount()); - map.put("DestroyCount", this.getDestroyCount()); - map.put("ConnectCount", this.getConnectCount()); - - // 5 - 9 - map.put("CloseCount", this.getCloseCount()); - map.put("ActiveCount", this.getActivePeak()); - map.put("PoolingCount", this.getPoolingCount()); - map.put("LockQueueLength", this.getLockQueueLength()); - map.put("WaitThreadCount", this.getNotEmptyWaitThreadPeak()); - - // 10 - 14 - map.put("InitialSize", this.getInitialSize()); - map.put("MaxActive", this.getMaxActive()); - map.put("MinIdle", this.getMinIdle()); - map.put("PoolPreparedStatements", this.isPoolPreparedStatements()); - map.put("TestOnBorrow", this.isTestOnBorrow()); - - // 15 - 19 - map.put("TestOnReturn", this.isTestOnReturn()); - map.put("MinEvictableIdleTimeMillis", this.getMinEvictableIdleTimeMillis()); - map.put("ConnectErrorCount", this.getConnectErrorCount()); - map.put("CreateTimespanMillis", this.getCreateTimespanMillis()); - map.put("DbType", this.getDbType()); - - // 20 - 24 - map.put("ValidationQuery", this.getValidationQuery()); - map.put("ValidationQueryTimeout", this.getValidationQueryTimeout()); - map.put("DriverClassName", this.getDriverClassName()); - map.put("Username", this.getUsername()); - map.put("RemoveAbandonedCount", this.getRemoveAbandonedCount()); - - // 25 - 29 - map.put("NotEmptyWaitCount", this.getNotEmptyWaitCount()); - map.put("NotEmptyWaitNanos", this.getNotEmptyWaitNanos()); - map.put("ErrorCount", this.getErrorCount()); - map.put("ReusePreparedStatementCount", this.getCachedPreparedStatementHitCount()); - map.put("StartTransactionCount", this.getStartTransactionCount()); - - // 30 - 34 - map.put("CommitCount", this.getCommitCount()); - map.put("RollbackCount", this.getRollbackCount()); - map.put("LastError", JMXUtils.getErrorCompositeData(this.getLastError())); - map.put("LastCreateError", JMXUtils.getErrorCompositeData(this.getLastCreateError())); - map.put("PreparedStatementCacheDeleteCount", this.getCachedPreparedStatementDeleteCount()); - - // 35 - 39 - map.put("PreparedStatementCacheAccessCount", this.getCachedPreparedStatementAccessCount()); - map.put("PreparedStatementCacheMissCount", this.getCachedPreparedStatementMissCount()); - map.put("PreparedStatementCacheHitCount", this.getCachedPreparedStatementHitCount()); - map.put("PreparedStatementCacheCurrentCount", this.getCachedPreparedStatementCount()); - map.put("Version", this.getVersion()); - - // 40 - - map.put("LastErrorTime", this.getLastErrorTime()); - map.put("LastCreateErrorTime", this.getLastCreateErrorTime()); - map.put("CreateErrorCount", this.getCreateErrorCount()); - map.put("DiscardCount", this.getDiscardCount()); - - return map; - } catch (JMException ex) { - throw new IllegalStateException("getStatData error", ex); - } - } - - public Map getStatData() { - final int activeCount; - final int activePeak; - final Date activePeakTime; - - final int poolingCount; - final int poolingPeak; - final Date poolingPeakTime; - - final long connectCount; - final long closeCount; - - lock.lock(); - try { - poolingCount = this.poolingCount; - poolingPeak = this.poolingPeak; - poolingPeakTime = this.getPoolingPeakTime(); - - activeCount = this.activeCount; - activePeak = this.activePeak; - activePeakTime = this.getActivePeakTime(); - - connectCount = this.connectCount; - closeCount = this.closeCount; - } finally { - lock.unlock(); - } - Map dataMap = new LinkedHashMap(); - - dataMap.put("Identity", System.identityHashCode(this)); - dataMap.put("Name", this.getName()); - dataMap.put("DbType", this.getDbType()); - dataMap.put("DriverClassName", this.getDriverClassName()); - - dataMap.put("URL", this.getUrl()); - dataMap.put("UserName", this.getUsername()); - dataMap.put("FilterClassNames", this.getFilterClassNames()); - - dataMap.put("WaitThreadCount", this.getWaitThreadCount()); - dataMap.put("NotEmptyWaitCount", this.getNotEmptyWaitCount()); - dataMap.put("NotEmptyWaitMillis", this.getNotEmptyWaitMillis()); - - dataMap.put("PoolingCount", poolingCount); - dataMap.put("PoolingPeak", poolingPeak); - dataMap.put("PoolingPeakTime", poolingPeakTime); - - dataMap.put("ActiveCount", activeCount); - dataMap.put("ActivePeak", activePeak); - dataMap.put("ActivePeakTime", activePeakTime); - - dataMap.put("InitialSize", this.getInitialSize()); - dataMap.put("MinIdle", this.getMinIdle()); - dataMap.put("MaxActive", this.getMaxActive()); - - dataMap.put("QueryTimeout", this.getQueryTimeout()); - dataMap.put("TransactionQueryTimeout", this.getTransactionQueryTimeout()); - dataMap.put("LoginTimeout", this.getLoginTimeout()); - dataMap.put("ValidConnectionCheckerClassName", this.getValidConnectionCheckerClassName()); - dataMap.put("ExceptionSorterClassName", this.getExceptionSorterClassName()); - - dataMap.put("TestOnBorrow", this.isTestOnBorrow()); - dataMap.put("TestOnReturn", this.isTestOnReturn()); - dataMap.put("TestWhileIdle", this.isTestWhileIdle()); - - dataMap.put("DefaultAutoCommit", this.isDefaultAutoCommit()); - dataMap.put("DefaultReadOnly", this.getDefaultReadOnly()); - dataMap.put("DefaultTransactionIsolation", this.getDefaultTransactionIsolation()); - - dataMap.put("LogicConnectCount", connectCount); - dataMap.put("LogicCloseCount", closeCount); - dataMap.put("LogicConnectErrorCount", this.getConnectErrorCount()); - - dataMap.put("PhysicalConnectCount", this.getCreateCount()); - dataMap.put("PhysicalCloseCount", this.getDestroyCount()); - dataMap.put("PhysicalConnectErrorCount", this.getCreateErrorCount()); - - dataMap.put("ExecuteCount", this.getExecuteCount()); - dataMap.put("ErrorCount", this.getErrorCount()); - dataMap.put("CommitCount", this.getCommitCount()); - dataMap.put("RollbackCount", this.getRollbackCount()); - - dataMap.put("PSCacheAccessCount", this.getCachedPreparedStatementAccessCount()); - dataMap.put("PSCacheHitCount", this.getCachedPreparedStatementHitCount()); - dataMap.put("PSCacheMissCount", this.getCachedPreparedStatementMissCount()); - - dataMap.put("StartTransactionCount", this.getStartTransactionCount()); - dataMap.put("TransactionHistogram", this.getTransactionHistogramValues()); - - dataMap.put("ConnectionHoldTimeHistogram", this.getDataSourceStat().getConnectionHoldHistogram().toArray()); - dataMap.put("RemoveAbandoned", this.isRemoveAbandoned()); - dataMap.put("ClobOpenCount", this.getDataSourceStat().getClobOpenCount()); - dataMap.put("BlobOpenCount", this.getDataSourceStat().getBlobOpenCount()); - - return dataMap; - } - - public JdbcSqlStat getSqlStat(int sqlId) { - return this.getDataSourceStat().getSqlStat(sqlId); - } - - public JdbcSqlStat getSqlStat(long sqlId) { - return this.getDataSourceStat().getSqlStat(sqlId); - } - - public Map getSqlStatMap() { - return this.getDataSourceStat().getSqlStatMap(); - } - - public Map getWallStatMap() { - WallProviderStatValue wallStatValue = getWallStatValue(false); - - if (wallStatValue != null) { - return wallStatValue.toMap(); - } - - return null; - } - - public WallProviderStatValue getWallStatValue(boolean reset) { - for (Filter filter : this.filters) { - if (filter instanceof WallFilter) { - WallFilter wallFilter = (WallFilter) filter; - return wallFilter.getProvider().getStatValue(reset); - } - } - - return null; - } - - public Lock getLock() { - return lock; - } - - @Override - public boolean isWrapperFor(Class iface) { - for (Filter filter : this.filters) { - if (filter.isWrapperFor(iface)) { - return true; - } - } - - if (this.statLogger != null - && (this.statLogger.getClass() == iface || DruidDataSourceStatLogger.class == iface)) { - return true; - } - - return super.isWrapperFor(iface); - } - - @SuppressWarnings("unchecked") - public T unwrap(Class iface) { - for (Filter filter : this.filters) { - if (filter.isWrapperFor(iface)) { - return (T) filter; - } - } - - if (this.statLogger != null - && (this.statLogger.getClass() == iface || DruidDataSourceStatLogger.class == iface)) { - return (T) statLogger; - } - - return super.unwrap(iface); - } -} +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.pool; + +import com.alibaba.druid.Constants; +import com.alibaba.druid.TransactionTimeoutException; +import com.alibaba.druid.VERSION; +import com.alibaba.druid.filter.AutoLoad; +import com.alibaba.druid.filter.Filter; +import com.alibaba.druid.filter.FilterChainImpl; +import com.alibaba.druid.mock.MockDriver; +import com.alibaba.druid.pool.DruidPooledPreparedStatement.PreparedStatementKey; +import com.alibaba.druid.pool.vendor.DB2ExceptionSorter; +import com.alibaba.druid.pool.vendor.InformixExceptionSorter; +import com.alibaba.druid.pool.vendor.MSSQLValidConnectionChecker; +import com.alibaba.druid.pool.vendor.MockExceptionSorter; +import com.alibaba.druid.pool.vendor.MySqlExceptionSorter; +import com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker; +import com.alibaba.druid.pool.vendor.OracleExceptionSorter; +import com.alibaba.druid.pool.vendor.OracleValidConnectionChecker; +import com.alibaba.druid.pool.vendor.SybaseExceptionSorter; +import com.alibaba.druid.proxy.DruidDriver; +import com.alibaba.druid.proxy.jdbc.DataSourceProxyConfig; +import com.alibaba.druid.proxy.jdbc.TransactionInfo; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.parser.SQLParserUtils; +import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.stat.DruidDataSourceStatManager; +import com.alibaba.druid.stat.JdbcDataSourceStat; +import com.alibaba.druid.stat.JdbcSqlStat; +import com.alibaba.druid.stat.JdbcSqlStatValue; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; +import com.alibaba.druid.util.JMXUtils; +import com.alibaba.druid.util.JdbcConstants; +import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.util.StringUtils; +import com.alibaba.druid.util.Utils; +import com.alibaba.druid.wall.WallFilter; +import com.alibaba.druid.wall.WallProviderStatValue; + +import javax.management.JMException; +import javax.management.ObjectName; +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.StringRefAddr; +import javax.sql.ConnectionEvent; +import javax.sql.ConnectionEventListener; +import javax.sql.ConnectionPoolDataSource; +import javax.sql.PooledConnection; +import java.io.Closeable; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.ConcurrentModificationException; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.ServiceLoader; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import static com.alibaba.druid.util.Utils.getBoolean; + +/** + * @author ljw + * @author wenshao + */ +public class DruidDataSource extends DruidAbstractDataSource implements DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource { + + private final static Log LOG = LogFactory.getLog(DruidDataSource.class); + + private static final long serialVersionUID = 1L; + + // stats + private final AtomicLong recycleErrorCount = new AtomicLong(); + private long connectCount = 0L; + private long closeCount = 0L; + private final AtomicLong connectErrorCount = new AtomicLong(); + private long recycleCount = 0L; + private long removeAbandonedCount = 0L; + private long notEmptyWaitCount = 0L; + private long notEmptySignalCount = 0L; + private long notEmptyWaitNanos = 0L; + + private int activePeak = 0; + private long activePeakTime = 0; + private int poolingPeak = 0; + private long poolingPeakTime = 0; + + // store + private volatile DruidConnectionHolder[] connections; + private int poolingCount = 0; + private int activeCount = 0; + private long discardCount = 0; + private int notEmptyWaitThreadCount = 0; + private int notEmptyWaitThreadPeak = 0; + + // threads + private CreateConnectionThread createConnectionThread; + private DestroyConnectionThread destroyConnectionThread; + private LogStatsThread logStatsThread; + + private final CountDownLatch initedLatch = new CountDownLatch(2); + + private volatile boolean enable = true; + + private boolean resetStatEnable = true; + private final AtomicLong resetCount = new AtomicLong(); + + private String initStackTrace; + + private volatile boolean closed = false; + private long closeTimeMillis = -1L; + + protected JdbcDataSourceStat dataSourceStat; + + private boolean useGlobalDataSourceStat = false; + + private boolean mbeanRegistered = false; + + public static ThreadLocal waitNanosLocal = new ThreadLocal(); + + private boolean logDifferentThread = true; + + public DruidDataSource(){ + this(false); + } + + public DruidDataSource(boolean fairLock){ + super(fairLock); + + configFromPropety(System.getProperties()); + } + + public void configFromPropety(Properties properties) { + { + Boolean value = getBoolean(properties, "druid.testWhileIdle"); + if (value != null) { + this.setTestWhileIdle(value); + } + } + { + Boolean value = getBoolean(properties, "druid.testOnBorrow"); + if (value != null) { + this.setTestOnBorrow(value); + } + } + { + String property = properties.getProperty("druid.validationQuery"); + if (property != null && property.length() > 0) { + this.setValidationQuery(property); + } + } + { + Boolean value = getBoolean(properties, "druid.useGlobalDataSourceStat"); + if (value != null) { + this.setUseGlobalDataSourceStat(value); + } + } + { + Boolean value = getBoolean(properties, "druid.useGloalDataSourceStat"); // compatible for early versions + if (value != null) { + this.setUseGlobalDataSourceStat(value); + } + } + { + String property = properties.getProperty("druid.filters"); + + if (property != null && property.length() > 0) { + try { + this.setFilters(property); + } catch (SQLException e) { + LOG.error("setFilters error", e); + } + } + } + { + String property = properties.getProperty(Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS); + if (property != null && property.length() > 0) { + try { + long value = Long.parseLong(property); + this.setTimeBetweenLogStatsMillis(value); + } catch (NumberFormatException e) { + LOG.error("illegal property '" + Constants.DRUID_TIME_BETWEEN_LOG_STATS_MILLIS + "'", e); + } + } + } + { + String property = properties.getProperty(Constants.DRUID_STAT_SQL_MAX_SIZE); + if (property != null && property.length() > 0) { + try { + int value = Integer.parseInt(property); + if (dataSourceStat != null) { + dataSourceStat.setMaxSqlSize(value); + } + } catch (NumberFormatException e) { + LOG.error("illegal property '" + Constants.DRUID_STAT_SQL_MAX_SIZE + "'", e); + } + } + } + { + Boolean value = getBoolean(properties, "druid.clearFiltersEnable"); + if (value != null) { + this.setClearFiltersEnable(value); + } + } + { + Boolean value = getBoolean(properties, "druid.resetStatEnable"); + if (value != null) { + this.setResetStatEnable(value); + } + } + { + String property = properties.getProperty("druid.notFullTimeoutRetryCount"); + if (property != null && property.length() > 0) { + try { + int value = Integer.parseInt(property); + this.setNotFullTimeoutRetryCount(value); + } catch (NumberFormatException e) { + LOG.error("illegal property 'druid.notFullTimeoutRetryCount'", e); + } + } + } + } + + public boolean isUseGlobalDataSourceStat() { + return useGlobalDataSourceStat; + } + + public void setUseGlobalDataSourceStat(boolean useGlobalDataSourceStat) { + this.useGlobalDataSourceStat = useGlobalDataSourceStat; + } + + public String getInitStackTrace() { + return initStackTrace; + } + + public boolean isResetStatEnable() { + return resetStatEnable; + } + + public void setResetStatEnable(boolean resetStatEnable) { + this.resetStatEnable = resetStatEnable; + if (dataSourceStat != null) { + dataSourceStat.setResetStatEnable(resetStatEnable); + } + } + + public long getDiscardCount() { + return discardCount; + } + + public void restart() { + lock.lock(); + try { + if (LOG.isInfoEnabled()) { + LOG.info("{dataSource-" + this.getID() + "} restart"); + } + + this.close(); + this.resetStat(); + this.inited = false; + this.enable = true; + this.closed = false; + } finally { + lock.unlock(); + } + } + + public void resetStat() { + if (!isResetStatEnable()) { + return; + } + + lock.lock(); + try { + connectCount = 0; + closeCount = 0; + discardCount = 0; + recycleCount = 0; + createCount.set(0); + destroyCount.set(0); + removeAbandonedCount = 0; + notEmptyWaitCount = 0; + notEmptySignalCount = 0L; + notEmptyWaitNanos = 0; + + activePeak = 0; + activePeakTime = 0; + poolingPeak = 0; + createTimespan = 0; + lastError = null; + lastErrorTimeMillis = 0; + lastCreateError = null; + lastCreateErrorTimeMillis = 0; + } finally { + lock.unlock(); + } + + connectErrorCount.set(0); + errorCount.set(0); + commitCount.set(0); + rollbackCount.set(0); + startTransactionCount.set(0); + cachedPreparedStatementHitCount.set(0); + closedPreparedStatementCount.set(0); + preparedStatementCount.set(0); + transactionHistogram.reset(); + cachedPreparedStatementDeleteCount.set(0); + recycleErrorCount.set(0); + + resetCount.incrementAndGet(); + } + + public long getResetCount() { + return this.resetCount.get(); + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + lock.lock(); + try { + this.enable = enable; + if (!enable) { + notEmpty.signalAll(); + notEmptySignalCount++; + } + } finally { + lock.unlock(); + } + } + + public void setPoolPreparedStatements(boolean value) { + if (this.poolPreparedStatements == value) { + return; + } + + this.poolPreparedStatements = value; + + if (!inited) { + return; + } + + if (LOG.isInfoEnabled()) { + LOG.info("set poolPreparedStatements " + this.poolPreparedStatements + " -> " + value); + } + + if (!value) { + lock.lock(); + try { + + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder connection = connections[i]; + + for (PreparedStatementHolder holder : connection.getStatementPool().getMap().values()) { + closePreapredStatement(holder); + } + + connection.getStatementPool().getMap().clear(); + } + } finally { + lock.unlock(); + } + } + } + + public void setMaxActive(int maxActive) { + if (this.maxActive == maxActive) { + return; + } + + if (maxActive == 0) { + throw new IllegalArgumentException("maxActive can't not set zero"); + } + + if (!inited) { + this.maxActive = maxActive; + return; + } + + if (maxActive < this.minIdle) { + throw new IllegalArgumentException("maxActive less than minIdle, " + maxActive + " < " + this.minIdle); + } + + if (LOG.isInfoEnabled()) { + LOG.info("maxActive changed : " + this.maxActive + " -> " + maxActive); + } + + lock.lock(); + try { + int allCount = this.poolingCount + this.activeCount; + + if (maxActive > allCount) { + this.connections = Arrays.copyOf(this.connections, maxActive); + } else { + this.connections = Arrays.copyOf(this.connections, allCount); + } + + this.maxActive = maxActive; + } finally { + lock.unlock(); + } + } + + @SuppressWarnings("rawtypes") + public void setConnectProperties(Properties properties) { + if (properties == null) { + properties = new Properties(); + } + + boolean equals; + if (properties.size() == this.connectProperties.size()) { + equals = true; + for (Map.Entry entry : properties.entrySet()) { + Object value = this.connectProperties.get(entry.getKey()); + Object entryValue = entry.getValue(); + if (value == null && entryValue != null) { + equals = false; + break; + } + + if (!value.equals(entry.getValue())) { + equals = false; + break; + } + } + } else { + equals = false; + } + + if (!equals) { + if (inited && LOG.isInfoEnabled()) { + LOG.info("connectProperties changed : " + this.connectProperties + " -> " + properties); + } + + configFromPropety(properties); + + for (Filter filter : this.filters) { + filter.configFromProperties(properties); + } + + if (exceptionSorter != null) { + exceptionSorter.configFromProperties(properties); + } + + if (validConnectionChecker != null) { + validConnectionChecker.configFromProperties(properties); + } + + if (statLogger != null) { + statLogger.configFromProperties(properties); + } + } + + this.connectProperties = properties; + } + + public boolean isInited() { + return this.inited; + } + + public void init() throws SQLException { + if (inited) { + return; + } + + final ReentrantLock lock = this.lock; + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + throw new SQLException("interrupt", e); + } + + boolean init = false; + try { + if (inited) { + return; + } + + init = true; + + initStackTrace = Utils.toString(Thread.currentThread().getStackTrace()); + + this.id = DruidDriver.createDataSourceId(); + if (this.id > 1) { + long delta = (this.id - 1) * 100000; + this.connectionIdSeed.addAndGet(delta); + this.statementIdSeed.addAndGet(delta); + this.resultSetIdSeed.addAndGet(delta); + this.transactionIdSeed.addAndGet(delta); + } + + if (this.jdbcUrl != null) { + this.jdbcUrl = this.jdbcUrl.trim(); + initFromWrapDriverUrl(); + } + + if (this.dbType == null || this.dbType.length() == 0) { + this.dbType = JdbcUtils.getDbType(jdbcUrl, null); + } + + for (Filter filter : filters) { + filter.init(this); + } + + if (JdbcConstants.MYSQL.equals(this.dbType) || // + JdbcConstants.MARIADB.equals(this.dbType)) { + boolean cacheServerConfigurationSet = false; + if (this.connectProperties.containsKey("cacheServerConfiguration")) { + cacheServerConfigurationSet = true; + } else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) { + cacheServerConfigurationSet = true; + } + if (cacheServerConfigurationSet) { + this.connectProperties.put("cacheServerConfiguration", "true"); + } + } + + if (maxActive <= 0) { + throw new IllegalArgumentException("illegal maxActive " + maxActive); + } + + if (maxActive < minIdle) { + throw new IllegalArgumentException("illegal maxActive " + maxActive); + } + + if (getInitialSize() > maxActive) { + throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActieve " + + maxActive); + } + + if (timeBetweenLogStatsMillis > 0 && useGlobalDataSourceStat) { + throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGlobalDataSourceStat=true"); + } + + if (this.driverClass != null) { + this.driverClass = driverClass.trim(); + } + + initFromSPIServiceLoader(); + + if (this.driver == null) { + if (this.driverClass == null || this.driverClass.isEmpty()) { + this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl); + } + + if (MockDriver.class.getName().equals(driverClass)) { + driver = MockDriver.instance; + } else { + driver = JdbcUtils.createDriver(driverClassLoader, driverClass); + } + } else { + if (this.driverClass == null) { + this.driverClass = driver.getClass().getName(); + } + } + + initCheck(); + + initExceptionSorter(); + initValidConnectionChecker(); + validationQueryCheck(); + + if (isUseGlobalDataSourceStat()) { + dataSourceStat = JdbcDataSourceStat.getGlobal(); + if (dataSourceStat == null) { + dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbType); + JdbcDataSourceStat.setGlobal(dataSourceStat); + } + if (dataSourceStat.getDbType() == null) { + dataSourceStat.setDbType(this.getDbType()); + } + } else { + dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbType, this.connectProperties); + } + dataSourceStat.setResetStatEnable(this.resetStatEnable); + + connections = new DruidConnectionHolder[maxActive]; + + SQLException connectError = null; + + try { + // init connections + for (int i = 0, size = getInitialSize(); i < size; ++i) { + Connection conn = createPhysicalConnection(); + DruidConnectionHolder holder = new DruidConnectionHolder(this, conn); + connections[poolingCount++] = holder; + } + + if (poolingCount > 0) { + poolingPeak = poolingCount; + poolingPeakTime = System.currentTimeMillis(); + } + } catch (SQLException ex) { + LOG.error("init datasource error", ex); + connectError = ex; + } + + createAndLogThread(); + createAndStartCreatorThread(); + createAndStartDestroyThread(); + + initedLatch.await(); + + initedTime = new Date(); + registerMbean(); + + if (connectError != null && poolingCount == 0) { + throw connectError; + } + } catch (SQLException e) { + LOG.error("dataSource init error", e); + throw e; + } catch (InterruptedException e) { + throw new SQLException(e.getMessage(), e); + } finally { + inited = true; + lock.unlock(); + + if (init && LOG.isInfoEnabled()) { + LOG.info("{dataSource-" + this.getID() + "} inited"); + } + } + } + + private void createAndLogThread() { + if (this.timeBetweenLogStatsMillis <= 0) { + return; + } + + String threadName = "Druid-ConnectionPool-Log-" + System.identityHashCode(this); + logStatsThread = new LogStatsThread(threadName); + logStatsThread.start(); + + this.resetStatEnable = false; + } + + private void createAndStartDestroyThread() { + String threadName = "Druid-ConnectionPool-Destroy-" + System.identityHashCode(this); + destroyConnectionThread = new DestroyConnectionThread(threadName); + destroyConnectionThread.start(); + } + + protected void createAndStartCreatorThread() { + String threadName = "Druid-ConnectionPool-Create-" + System.identityHashCode(this); + createConnectionThread = new CreateConnectionThread(threadName); + createConnectionThread.start(); + } + + /** + * load filters from SPI ServiceLoader + * + * @see ServiceLoader + */ + private void initFromSPIServiceLoader() { + + String property = System.getProperty("druid.load.spifilter.skip"); + if (property != null) { + return; + } + + ServiceLoader druidAutoFilterLoader = ServiceLoader.load(Filter.class); + + for (Filter autoFilter : druidAutoFilterLoader) { + AutoLoad autoLoad = autoFilter.getClass().getAnnotation(AutoLoad.class); + if (autoLoad != null && autoLoad.value()) { + if (LOG.isInfoEnabled()) { + LOG.info("load filter from spi :" + autoFilter.getClass().getName()); + } + addFilter(autoFilter); + } + } + } + + private void initFromWrapDriverUrl() throws SQLException { + if (!jdbcUrl.startsWith(DruidDriver.DEFAULT_PREFIX)) { + return; + } + + DataSourceProxyConfig config = DruidDriver.parseConfig(jdbcUrl, null); + this.driverClass = config.getRawDriverClassName(); + + LOG.error("error url : '" + jdbcUrl + "', it should be : '" + config.getRawUrl() + "'"); + + this.jdbcUrl = config.getRawUrl(); + if (this.name == null) { + this.name = config.getName(); + } + + for (Filter filter : config.getFilters()) { + addFilter(filter); + } + } + + /** + * 会去重复 + * + * @param filter + */ + private void addFilter(Filter filter) { + boolean exists = false; + for (Filter initedFilter : this.filters) { + if (initedFilter.getClass() == filter.getClass()) { + exists = true; + break; + } + } + + if (!exists) { + filter.init(this); + this.filters.add(filter); + } + + } + + private void validationQueryCheck() { + if (!(isTestOnBorrow() || isTestOnReturn() || isTestWhileIdle())) { + return; + } + + if (this.validConnectionChecker != null) { + return; + } + + if (this.getValidationQuery() != null && this.getValidationQuery().length() > 0) { + return; + } + + String errorMessage = ""; + + if (isTestOnBorrow()) { + errorMessage += "testOnBorrow is true, "; + } + + if (isTestOnReturn()) { + errorMessage += "testOnReturn is true, "; + } + + if (isTestWhileIdle()) { + errorMessage += "testWhileIdle is true, "; + } + + LOG.error(errorMessage + "validationQuery not set"); + } + + protected void initCheck() throws SQLException { + if (JdbcUtils.ORACLE.equals(this.dbType)) { + isOracle = true; + + if (driver.getMajorVersion() < 10) { + throw new SQLException("not support oracle driver " + driver.getMajorVersion() + "." + + driver.getMinorVersion()); + } + + if (driver.getMajorVersion() == 10 && isUseOracleImplicitCache()) { + this.getConnectProperties().setProperty("oracle.jdbc.FreeMemoryOnEnterImplicitCache", "true"); + } + + oracleValidationQueryCheck(); + } else if (JdbcUtils.DB2.equals(dbType)) { + db2ValidationQueryCheck(); + } + } + + private void oracleValidationQueryCheck() { + if (validationQuery == null) { + return; + } + if (validationQuery.length() == 0) { + return; + } + + SQLStatementParser sqlStmtParser = SQLParserUtils.createSQLStatementParser(validationQuery, this.dbType); + List stmtList = sqlStmtParser.parseStatementList(); + + if (stmtList.size() != 1) { + return; + } + + SQLStatement stmt = stmtList.get(0); + if (!(stmt instanceof SQLSelectStatement)) { + return; + } + + SQLSelectQuery query = ((SQLSelectStatement) stmt).getSelect().getQuery(); + if (query instanceof SQLSelectQueryBlock) { + if (((SQLSelectQueryBlock) query).getFrom() == null) { + LOG.error("invalid oracle validationQuery. " + validationQuery + ", may should be : " + validationQuery + + " FROM DUAL"); + } + } + } + + private void db2ValidationQueryCheck() { + if (validationQuery == null) { + return; + } + if (validationQuery.length() == 0) { + return; + } + + SQLStatementParser sqlStmtParser = SQLParserUtils.createSQLStatementParser(validationQuery, this.dbType); + List stmtList = sqlStmtParser.parseStatementList(); + + if (stmtList.size() != 1) { + return; + } + + SQLStatement stmt = stmtList.get(0); + if (!(stmt instanceof SQLSelectStatement)) { + return; + } + + SQLSelectQuery query = ((SQLSelectStatement) stmt).getSelect().getQuery(); + if (query instanceof SQLSelectQueryBlock) { + if (((SQLSelectQueryBlock) query).getFrom() == null) { + LOG.error("invalid db2 validationQuery. " + validationQuery + ", may should be : " + validationQuery + + " FROM SYSDUMMY"); + } + } + } + + private void initValidConnectionChecker() { + String realDriverClassName = driver.getClass().getName(); + if (realDriverClassName.equals("com.mysql.jdbc.Driver")) { + this.validConnectionChecker = new MySqlValidConnectionChecker(); + } else if (realDriverClassName.equals("oracle.jdbc.driver.OracleDriver")) { + this.validConnectionChecker = new OracleValidConnectionChecker(); + } else if (realDriverClassName.equals("com.microsoft.jdbc.sqlserver.SQLServerDriver")) { + this.validConnectionChecker = new MSSQLValidConnectionChecker(); + } + } + + private void initExceptionSorter() { + String realDriverClassName = driver.getClass().getName(); + if (realDriverClassName.equals(JdbcConstants.MYSQL_DRIVER)) { + this.exceptionSorter = new MySqlExceptionSorter(); + } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER)) { + this.exceptionSorter = new OracleExceptionSorter(); + } else if (realDriverClassName.equals("com.informix.jdbc.IfxDriver")) { + this.exceptionSorter = new InformixExceptionSorter(); + + } else if (realDriverClassName.equals("com.sybase.jdbc2.jdbc.SybDriver")) { + this.exceptionSorter = new SybaseExceptionSorter(); + + } else if (realDriverClassName.equals("com.alibaba.druid.mock.MockDriver")) { + this.exceptionSorter = new MockExceptionSorter(); + } else if (realDriverClassName.contains("DB2")) { + this.exceptionSorter = new DB2ExceptionSorter(); + } + } + + @Override + public DruidPooledConnection getConnection() throws SQLException { + return getConnection(maxWait); + } + + public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException { + init(); + + if (filters.size() > 0) { + FilterChainImpl filterChain = new FilterChainImpl(this); + return filterChain.dataSource_connect(this, maxWaitMillis); + } else { + return getConnectionDirect(maxWaitMillis); + } + } + + @Override + public PooledConnection getPooledConnection() throws SQLException { + return getConnection(maxWait); + } + + @Override + public PooledConnection getPooledConnection(String user, String password) throws SQLException { + throw new UnsupportedOperationException("Not supported by DruidDataSource"); + } + + public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException { + int notFullTimeoutRetryCnt = 0; + for (;;) { + // handle notFullTimeoutRetry + DruidPooledConnection poolableConnection; + try { + poolableConnection = getConnectionInternal(maxWaitMillis); + } catch (GetConnectionTimeoutException ex) { + if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) { + notFullTimeoutRetryCnt++; + if (LOG.isWarnEnabled()) { + LOG.warn("not full timeout retry : " + notFullTimeoutRetryCnt); + } + continue; + } + throw ex; + } + + if (isTestOnBorrow()) { + boolean validate = testConnectionInternal(poolableConnection.getConnection()); + if (!validate) { + if (LOG.isDebugEnabled()) { + LOG.debug("skip not validate connection."); + } + + Connection realConnection = poolableConnection.getConnection(); + discardConnection(realConnection); + continue; + } + } else { + Connection realConnection = poolableConnection.getConnection(); + if (realConnection.isClosed()) { + discardConnection(null); // 传入null,避免重复关闭 + continue; + } + + if (isTestWhileIdle()) { + final long currentTimeMillis = System.currentTimeMillis(); + final long lastActiveTimeMillis = poolableConnection.getConnectionHolder().getLastActiveTimeMillis(); + final long idleMillis = currentTimeMillis - lastActiveTimeMillis; + long timeBetweenEvictionRunsMillis = this.getTimeBetweenEvictionRunsMillis(); + if (timeBetweenEvictionRunsMillis <= 0) { + timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; + } + + if (idleMillis >= timeBetweenEvictionRunsMillis) { + boolean validate = testConnectionInternal(poolableConnection.getConnection()); + if (!validate) { + if (LOG.isDebugEnabled()) { + LOG.debug("skip not validate connection."); + } + + discardConnection(realConnection); + continue; + } + } + } + } + + if (isRemoveAbandoned()) { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + poolableConnection.setConnectStackTrace(stackTrace); + poolableConnection.setConnectedTimeNano(); + poolableConnection.setTraceEnable(true); + + synchronized (activeConnections) { + activeConnections.put(poolableConnection, PRESENT); + } + } + + if (!this.isDefaultAutoCommit()) { + poolableConnection.setAutoCommit(false); + } + + return poolableConnection; + } + } + + /** + * 抛弃连接,不进行回收,而是抛弃 + * + * @param realConnection + * @throws SQLException + */ + public void discardConnection(Connection realConnection) { + JdbcUtils.close(realConnection); + + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + LOG.error("interrupt"); + return; + } + try { + activeCount--; + discardCount++; + + if (activeCount <= 0) { + empty.signal(); + } + } finally { + lock.unlock(); + } + } + + private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException { + if (closed) { + connectErrorCount.incrementAndGet(); + throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis)); + } + + if (!enable) { + connectErrorCount.incrementAndGet(); + throw new DataSourceDisableException(); + } + + final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait); + final int maxWaitThreadCount = getMaxWaitThreadCount(); + + DruidConnectionHolder holder; + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + connectErrorCount.incrementAndGet(); + throw new SQLException("interrupt", e); + } + + try { + if (maxWaitThreadCount > 0) { + if (notEmptyWaitThreadCount >= maxWaitThreadCount) { + connectErrorCount.incrementAndGet(); + throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count " + + lock.getQueueLength()); + } + } + + connectCount++; + + if (maxWait > 0) { + holder = pollLast(nanos); + } else { + holder = takeLast(); + } + + if (holder != null) { + activeCount++; + if (activeCount > activePeak) { + activePeak = activeCount; + activePeakTime = System.currentTimeMillis(); + } + } + } catch (InterruptedException e) { + connectErrorCount.incrementAndGet(); + throw new SQLException(e.getMessage(), e); + } catch (SQLException e) { + connectErrorCount.incrementAndGet(); + throw e; + } finally { + lock.unlock(); + } + + if (holder == null) { + long waitNanos = waitNanosLocal.get(); + + StringBuilder buf = new StringBuilder(); + buf.append("wait millis ")// + .append(waitNanos / (1000 * 1000))// + .append(", active " + activeCount)// + ; + + List sqlList = this.getDataSourceStat().getRuningSqlList(); + for (int i = 0; i < sqlList.size(); ++i) { + if (i != 0) { + buf.append('\n'); + } else { + buf.append(", "); + } + JdbcSqlStatValue sql = sqlList.get(i); + buf.append("runningSqlCount "); + buf.append(sql.getRunningCount()); + buf.append(" : "); + buf.append(sql.getSql()); + } + + String errorMessage = buf.toString(); + + if (this.createError != null) { + throw new GetConnectionTimeoutException(errorMessage, createError); + } else { + throw new GetConnectionTimeoutException(errorMessage); + } + } + + holder.incrementUseCount(); + + DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder); + return poolalbeConnection; + } + + public void handleConnectionException(DruidPooledConnection pooledConnection, Throwable t) throws SQLException { + final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); + + errorCount.incrementAndGet(); + lastError = t; + lastErrorTimeMillis = System.currentTimeMillis(); + + if (t instanceof SQLException) { + SQLException sqlEx = (SQLException) t; + + // broadcastConnectionError + ConnectionEvent event = new ConnectionEvent(pooledConnection, sqlEx); + for (ConnectionEventListener eventListener : holder.getConnectionEventListeners()) { + eventListener.connectionErrorOccurred(event); + } + + // exceptionSorter.isExceptionFatal + if (exceptionSorter != null && exceptionSorter.isExceptionFatal(sqlEx)) { + if (pooledConnection.isTraceEnable()) { + synchronized (activeConnections) { + if (pooledConnection.isTraceEnable()) { + activeConnections.remove(pooledConnection); + pooledConnection.setTraceEnable(false); + } + } + } + this.discardConnection(holder.getConnection()); + holder.setDiscard(true); + pooledConnection.disable(t); + + LOG.error("discard connection", sqlEx); + } + + throw sqlEx; + } else { + throw new SQLException("Error", t); + } + } + + /** + * 回收连接 + */ + protected void recycle(DruidPooledConnection pooledConnection) throws SQLException { + final DruidConnectionHolder holder = pooledConnection.getConnectionHolder(); + + if (holder == null) { + LOG.warn("connectionHolder is null"); + return; + } + + if (logDifferentThread && pooledConnection.getOwnerThread() != Thread.currentThread()) { + LOG.warn("get/close not same thread"); + } + + final Connection physicalConnection = holder.getConnection(); + + if (pooledConnection.isTraceEnable()) { + synchronized (activeConnections) { + if (pooledConnection.isTraceEnable()) { + Object oldInfo = activeConnections.remove(pooledConnection); + if (oldInfo == null) { + if (LOG.isWarnEnabled()) { + LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size()); + } + } + pooledConnection.setTraceEnable(false); + } + } + } + + final boolean isAutoCommit = holder.isUnderlyingAutoCommit(); + final boolean isReadOnly = holder.isUnderlyingReadOnly(); + final boolean testOnReturn = this.isTestOnReturn(); + + try { + // check need to rollback? + if ((!isAutoCommit) && (!isReadOnly)) { + pooledConnection.rollback(); + } + + // reset holder, restore default settings, clear warnings + holder.reset(); + + if (testOnReturn) { + boolean validate = testConnectionInternal(physicalConnection); + if (!validate) { + JdbcUtils.close(physicalConnection); + + destroyCount.incrementAndGet(); + + lock.lockInterruptibly(); + try { + activeCount--; + closeCount++; + } finally { + lock.unlock(); + } + return; + } + } + + if (!enable) { + discardConnection(holder.getConnection()); + return; + } + + final long lastActiveTimeMillis = System.currentTimeMillis(); + lock.lockInterruptibly(); + try { + activeCount--; + closeCount++; + + putLast(holder, lastActiveTimeMillis); + recycleCount++; + } finally { + lock.unlock(); + } + } catch (Throwable e) { + holder.clearStatementCache(); + + if (!holder.isDiscard()) { + this.discardConnection(physicalConnection); + holder.setDiscard(true); + } + + LOG.error("recyle error", e); + recycleErrorCount.incrementAndGet(); + } + } + + public long getRecycleErrorCount() { + return recycleErrorCount.get(); + } + + public void clearStatementCache() throws SQLException { + lock.lock(); + try { + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder conn = connections[i]; + conn.getStatementPool().clear(); + } + } finally { + lock.unlock(); + } + } + + /** + * close datasource + */ + public void close() { + lock.lock(); + try { + if (this.closed) { + return; + } + + if (!this.inited) { + return; + } + + if (logStatsThread != null) { + logStatsThread.interrupt(); + } + + if (createConnectionThread != null) { + createConnectionThread.interrupt(); + } + + if (destroyConnectionThread != null) { + destroyConnectionThread.interrupt(); + } + + for (int i = 0; i < poolingCount; ++i) { + try { + DruidConnectionHolder connHolder = connections[i]; + + for (PreparedStatementHolder stmtHolder : connHolder.getStatementPool().getMap().values()) { + connHolder.getStatementPool().closeRemovedStatement(stmtHolder); + } + connHolder.getStatementPool().getMap().clear(); + + Connection physicalConnection = connHolder.getConnection(); + physicalConnection.close(); + connections[i] = null; + destroyCount.incrementAndGet(); + } catch (Exception ex) { + LOG.warn("close connection error", ex); + } + } + poolingCount = 0; + unregisterMbean(); + + enable = false; + notEmpty.signalAll(); + notEmptySignalCount++; + + this.closed = true; + this.closeTimeMillis = System.currentTimeMillis(); + + for (Filter filter : filters) { + filter.destroy(); + } + } finally { + lock.unlock(); + } + + if (LOG.isInfoEnabled()) { + LOG.info("{dataSource-" + this.getID() + "} closed"); + } + } + + public void registerMbean() { + if (!mbeanRegistered) { + AccessController.doPrivileged(new PrivilegedAction() { + + @Override + public Object run() { + ObjectName objectName = DruidDataSourceStatManager.addDataSource(DruidDataSource.this, + DruidDataSource.this.name); + DruidDataSource.this.setObjectName(objectName); + DruidDataSource.this.mbeanRegistered = true; + return null; + } + }); + } + } + + public void unregisterMbean() { + if (mbeanRegistered) { + AccessController.doPrivileged(new PrivilegedAction() { + + @Override + public Object run() { + DruidDataSourceStatManager.removeDataSource(DruidDataSource.this); + DruidDataSource.this.mbeanRegistered = false; + return null; + } + }); + } + } + + public boolean isMbeanRegistered() { + return mbeanRegistered; + } + + void putLast(DruidConnectionHolder e, long lastActiveTimeMillis) { + e.setLastActiveTimeMillis(lastActiveTimeMillis); + connections[poolingCount++] = e; + + if (poolingCount > poolingPeak) { + poolingPeak = poolingCount; + poolingPeakTime = lastActiveTimeMillis; + } + + notEmpty.signal(); + notEmptySignalCount++; + } + + DruidConnectionHolder takeLast() throws InterruptedException, SQLException { + try { + while (poolingCount == 0) { + empty.signal(); // send signal to CreateThread create connection + notEmptyWaitThreadCount++; + if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) { + notEmptyWaitThreadPeak = notEmptyWaitThreadCount; + } + try { + notEmpty.await(); // signal by recycle or creator + } finally { + notEmptyWaitThreadCount--; + } + notEmptyWaitCount++; + + if (!enable) { + connectErrorCount.incrementAndGet(); + throw new DataSourceDisableException(); + } + } + } catch (InterruptedException ie) { + notEmpty.signal(); // propagate to non-interrupted thread + notEmptySignalCount++; + throw ie; + } + + poolingCount--; + DruidConnectionHolder last = connections[poolingCount]; + connections[poolingCount] = null; + + return last; + } + + DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException { + long estimate = nanos; + + for (;;) { + if (poolingCount == 0) { + empty.signal(); // send signal to CreateThread create connection + + if (estimate <= 0) { + waitNanosLocal.set(nanos - estimate); + return null; + } + + notEmptyWaitThreadCount++; + if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) { + notEmptyWaitThreadPeak = notEmptyWaitThreadCount; + } + + try { + long startEstimate = estimate; + estimate = notEmpty.awaitNanos(estimate); // signal by + // recycle or + // creator + notEmptyWaitCount++; + notEmptyWaitNanos += (startEstimate - estimate); + + if (!enable) { + connectErrorCount.incrementAndGet(); + throw new DataSourceDisableException(); + } + } catch (InterruptedException ie) { + notEmpty.signal(); // propagate to non-interrupted thread + notEmptySignalCount++; + throw ie; + } finally { + notEmptyWaitThreadCount--; + } + + if (poolingCount == 0) { + if (estimate > 0) { + continue; + } + + waitNanosLocal.set(nanos - estimate); + return null; + } + } + + poolingCount--; + DruidConnectionHolder last = connections[poolingCount]; + connections[poolingCount] = null; + + return last; + } + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + if (!StringUtils.equals(username, this.username)) { + throw new UnsupportedOperationException("Not supported by DruidDataSource"); + } + + if (!StringUtils.equals(password, this.password)) { + throw new UnsupportedOperationException("Not supported by DruidDataSource"); + } + + return getConnection(); + } + + public long getCreateCount() { + return createCount.get(); + } + + public long getDestroyCount() { + return destroyCount.get(); + } + + public long getConnectCount() { + lock.lock(); + try { + return connectCount; + } finally { + lock.unlock(); + } + } + + public long getCloseCount() { + return closeCount; + } + + public long getConnectErrorCount() { + return connectErrorCount.get(); + } + + @Override + public int getPoolingCount() { + lock.lock(); + try { + return poolingCount; + } finally { + lock.unlock(); + } + } + + public int getPoolingPeak() { + lock.lock(); + try { + return poolingPeak; + } finally { + lock.unlock(); + } + } + + public Date getPoolingPeakTime() { + if (poolingPeakTime <= 0) { + return null; + } + + return new Date(poolingPeakTime); + } + + public long getRecycleCount() { + return recycleCount; + } + + public int getActiveCount() { + lock.lock(); + try { + return activeCount; + } finally { + lock.unlock(); + } + } + + public void logStats() { + final DruidDataSourceStatLogger statLogger = this.statLogger; + if (statLogger == null) { + return; + } + + DruidDataSourceStatValue statValue = getStatValueAndReset(); + + statLogger.log(statValue); + } + + public DruidDataSourceStatValue getStatValueAndReset() { + DruidDataSourceStatValue value = new DruidDataSourceStatValue(); + + lock.lock(); + try { + value.setPoolingCount(this.poolingCount); + value.setPoolingPeak(this.poolingPeak); + value.setPoolingPeakTime(this.poolingPeakTime); + + value.setActiveCount(this.activeCount); + value.setActivePeak(this.activePeak); + value.setActivePeakTime(this.activePeakTime); + + value.setConnectCount(this.connectCount); + value.setCloseCount(this.closeCount); + value.setWaitThreadCount(lock.getWaitQueueLength(notEmpty)); + value.setNotEmptyWaitCount(this.notEmptyWaitCount); + value.setNotEmptyWaitNanos(this.notEmptyWaitNanos); + + // reset + this.poolingPeak = 0; + this.poolingPeakTime = 0; + this.activePeak = 0; + this.activePeakTime = 0; + this.connectCount = 0; + this.closeCount = 0; + + this.notEmptyWaitCount = 0; + this.notEmptyWaitNanos = 0; + } finally { + lock.unlock(); + } + + value.setName(this.getName()); + value.setDbType(this.getDbType()); + value.setDriverClassName(this.getDriverClassName()); + + value.setUrl(this.getUrl()); + value.setUserName(this.getUsername()); + value.setFilterClassNames(this.getFilterClassNames()); + + value.setInitialSize(this.getInitialSize()); + value.setMinIdle(this.getMinIdle()); + value.setMaxActive(this.getMaxActive()); + + value.setQueryTimeout(this.getQueryTimeout()); + value.setTransactionQueryTimeout(this.getTransactionQueryTimeout()); + value.setLoginTimeout(this.getLoginTimeout()); + value.setValidConnectionCheckerClassName(this.getValidConnectionCheckerClassName()); + value.setExceptionSorterClassName(this.getExceptionSorterClassName()); + + value.setTestOnBorrow(this.isTestOnBorrow()); + value.setTestOnReturn(this.isTestOnReturn()); + value.setTestWhileIdle(this.isTestWhileIdle()); + + value.setDefaultAutoCommit(this.isDefaultAutoCommit()); + + if (defaultReadOnly != null) { + value.setDefaultReadOnly(defaultReadOnly); + } + value.setDefaultTransactionIsolation(this.getDefaultTransactionIsolation()); + + value.setLogicConnectErrorCount(connectErrorCount.getAndSet(0)); + + value.setPhysicalConnectCount(createCount.getAndSet(0)); + value.setPhysicalCloseCount(destroyCount.getAndSet(0)); + value.setPhysicalConnectErrorCount(createErrorCount.getAndSet(0)); + + value.setExecuteCount(this.executeCount.getAndSet(0)); + value.setErrorCount(errorCount.getAndSet(0)); + value.setCommitCount(commitCount.getAndSet(0)); + value.setRollbackCount(rollbackCount.getAndSet(0)); + + value.setPstmtCacheHitCount(cachedPreparedStatementHitCount.getAndSet(0)); + value.setPstmtCacheMissCount(cachedPreparedStatementMissCount.getAndSet(0)); + + value.setStartTransactionCount(startTransactionCount.getAndSet(0)); + value.setTransactionHistogram(this.getTransactionHistogram().toArrayAndReset()); + + value.setConnectionHoldTimeHistogram(this.getDataSourceStat().getConnectionHoldHistogram().toArrayAndReset()); + value.removeAbandoned = this.isRemoveAbandoned(); + value.setClobOpenCount(this.getDataSourceStat().getClobOpenCountAndReset()); + value.setBlobOpenCount(this.getDataSourceStat().getBlobOpenCountAndReset()); + + value.setSqlSkipCount(this.getDataSourceStat().getSkipSqlCountAndReset()); + value.setSqlList(this.getDataSourceStat().getSqlStatMapAndReset()); + + return value; + } + + public long getRemoveAbandonedCount() { + return removeAbandonedCount; + } + + public class CreateConnectionThread extends Thread { + + public CreateConnectionThread(String name){ + super(name); + this.setDaemon(true); + } + + public void run() { + initedLatch.countDown(); + + int errorCount = 0; + for (;;) { + // addLast + try { + lock.lockInterruptibly(); + } catch (InterruptedException e2) { + break; + } + + try { + // 必须存在线程等待,才创建连接 + if (poolingCount >= notEmptyWaitThreadCount) { + empty.await(); + } + + // 防止创建超过maxActive数量的连接 + if (activeCount + poolingCount >= maxActive) { + empty.await(); + continue; + } + + } catch (InterruptedException e) { + lastCreateError = e; + lastErrorTimeMillis = System.currentTimeMillis(); + break; + } finally { + lock.unlock(); + } + + Connection connection = null; + + try { + connection = createPhysicalConnection(); + } catch (SQLException e) { + LOG.error("create connection error", e); + + errorCount++; + + if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) { + if (breakAfterAcquireFailure) { + break; + } + + try { + Thread.sleep(timeBetweenConnectErrorMillis); + } catch (InterruptedException interruptEx) { + break; + } + } + } catch (RuntimeException e) { + LOG.error("create connection error", e); + continue; + } catch (Error e) { + LOG.error("create connection error", e); + break; + } + + if (connection == null) { + continue; + } + + DruidConnectionHolder holder = null; + try { + holder = new DruidConnectionHolder(DruidDataSource.this, connection); + } catch (SQLException ex) { + LOG.error("create connection holder error", ex); + break; + } + + lock.lock(); + try { + connections[poolingCount++] = holder; + + if (poolingCount > poolingPeak) { + poolingPeak = poolingCount; + poolingPeakTime = System.currentTimeMillis(); + } + + errorCount = 0; // reset errorCount + + notEmpty.signal(); + notEmptySignalCount++; + } finally { + lock.unlock(); + } + } + } + } + + public class DestroyConnectionThread extends Thread { + + public DestroyConnectionThread(String name){ + super(name); + this.setDaemon(true); + } + + public void run() { + initedLatch.countDown(); + + for (;;) { + // 从前面开始删除 + try { + if (closed) { + break; + } + + if (timeBetweenEvictionRunsMillis > 0) { + Thread.sleep(timeBetweenEvictionRunsMillis); + } else { + Thread.sleep(1000); // + } + + if (Thread.interrupted()) { + break; + } + + shrink(true); + + if (isRemoveAbandoned()) { + removeAbandoned(); + } + } catch (InterruptedException e) { + break; + } + } + } + + } + + public class LogStatsThread extends Thread { + + public LogStatsThread(String name){ + super(name); + this.setDaemon(true); + } + + public void run() { + try { + for (;;) { + try { + logStats(); + } catch (Exception e) { + LOG.error("logStats error", e); + } + + Thread.sleep(timeBetweenLogStatsMillis); + } + } catch (InterruptedException e) { + // skip + } + } + } + + public int removeAbandoned() { + int removeCount = 0; + + long currrentNanos = System.nanoTime(); + + List abandonedList = new ArrayList(); + + synchronized (activeConnections) { + Iterator iter = activeConnections.keySet().iterator(); + + for (; iter.hasNext();) { + DruidPooledConnection pooledConnection = iter.next(); + + if (pooledConnection.isRunning()) { + continue; + } + + long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000); + + if (timeMillis >= removeAbandonedTimeoutMillis) { + iter.remove(); + pooledConnection.setTraceEnable(false); + abandonedList.add(pooledConnection); + } + } + } + + if (abandonedList.size() > 0) { + for (DruidPooledConnection pooledConnection : abandonedList) { + JdbcUtils.close(pooledConnection); + pooledConnection.abandond(); + removeAbandonedCount++; + removeCount++; + + if (isLogAbandoned()) { + StringBuilder buf = new StringBuilder(); + buf.append("abandon connection, open stackTrace\n"); + + StackTraceElement[] trace = pooledConnection.getConnectStackTrace(); + for (int i = 0; i < trace.length; i++) { + buf.append("\tat "); + buf.append(trace[i].toString()); + buf.append("\n"); + } + + LOG.error(buf.toString()); + } + } + } + + return removeCount; + } + + /** Instance key */ + protected String instanceKey = null; + + public Reference getReference() throws NamingException { + final String className = getClass().getName(); + final String factoryName = className + "Factory"; // XXX: not robust + Reference ref = new Reference(className, factoryName, null); + ref.add(new StringRefAddr("instanceKey", instanceKey)); + ref.add(new StringRefAddr("url", this.getUrl())); + ref.add(new StringRefAddr("username", this.getUsername())); + ref.add(new StringRefAddr("password", this.getPassword())); + // TODO ADD OTHER PROPERTIES + return ref; + } + + @Override + public List getFilterClassNames() { + List names = new ArrayList(); + for (Filter filter : filters) { + names.add(filter.getClass().getName()); + } + return names; + } + + public int getRawDriverMajorVersion() { + int version = -1; + if (this.driver != null) { + version = driver.getMajorVersion(); + } + return version; + } + + public int getRawDriverMinorVersion() { + int version = -1; + if (this.driver != null) { + version = driver.getMinorVersion(); + } + return version; + } + + public String getProperties() { + Properties properties = new Properties(); + properties.putAll(connectProperties); + if (properties.containsKey("password")) { + properties.put("password", "******"); + } + return properties.toString(); + } + + @Override + public void shrink() { + shrink(false); + } + + public void shrink(boolean checkTime) { + final List evictList = new ArrayList(); + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + return; + } + + try { + final int checkCount = poolingCount - minIdle; + final long currentTimeMillis = System.currentTimeMillis(); + for (int i = 0; i < checkCount; ++i) { + DruidConnectionHolder connection = connections[i]; + + if (checkTime) { + long idleMillis = currentTimeMillis - connection.getLastActiveTimeMillis(); + if (idleMillis >= minEvictableIdleTimeMillis) { + evictList.add(connection); + } else { + break; + } + } else { + evictList.add(connection); + } + } + + int removeCount = evictList.size(); + if (removeCount > 0) { + System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount); + Arrays.fill(connections, poolingCount - removeCount, poolingCount, null); + poolingCount -= removeCount; + } + } finally { + lock.unlock(); + } + + for (DruidConnectionHolder item : evictList) { + Connection connection = item.getConnection(); + JdbcUtils.close(connection); + destroyCount.incrementAndGet(); + } + } + + public int getWaitThreadCount() { + lock.lock(); + try { + return lock.getWaitQueueLength(notEmpty); + } finally { + lock.unlock(); + } + } + + public long getNotEmptyWaitCount() { + return notEmptyWaitCount; + } + + public int getNotEmptyWaitThreadCount() { + lock.lock(); + try { + return notEmptyWaitThreadCount; + } finally { + lock.unlock(); + } + } + + public int getNotEmptyWaitThreadPeak() { + lock.lock(); + try { + return notEmptyWaitThreadPeak; + } finally { + lock.unlock(); + } + } + + public long getNotEmptySignalCount() { + return notEmptySignalCount; + } + + public long getNotEmptyWaitMillis() { + return notEmptyWaitNanos / (1000 * 1000); + } + + public long getNotEmptyWaitNanos() { + return notEmptyWaitNanos; + } + + public int getLockQueueLength() { + return lock.getQueueLength(); + } + + public int getActivePeak() { + return activePeak; + } + + public Date getActivePeakTime() { + if (activePeakTime <= 0) { + return null; + } + + return new Date(activePeakTime); + } + + public String dump() { + lock.lock(); + try { + return this.toString(); + } finally { + lock.unlock(); + } + } + + public long getErrorCount() { + return this.errorCount.get(); + } + + public String toString() { + StringBuilder buf = new StringBuilder(); + + buf.append("{"); + + buf.append("\n\tCreateTime:\""); + buf.append(Utils.toString(getCreatedTime())); + buf.append("\""); + + buf.append(",\n\tActiveCount:"); + buf.append(getActiveCount()); + + buf.append(",\n\tPoolingCount:"); + buf.append(getPoolingCount()); + + buf.append(",\n\tCreateCount:"); + buf.append(getCreateCount()); + + buf.append(",\n\tDestroyCount:"); + buf.append(getDestroyCount()); + + buf.append(",\n\tCloseCount:"); + buf.append(getCloseCount()); + + buf.append(",\n\tConnectCount:"); + buf.append(getConnectCount()); + + buf.append(",\n\tConnections:["); + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder conn = connections[i]; + if (conn != null) { + if (i != 0) { + buf.append(","); + } + buf.append("\n\t\t"); + buf.append(conn.toString()); + } + } + buf.append("\n\t]"); + + buf.append("\n}"); + + if (this.isPoolPreparedStatements()) { + buf.append("\n\n["); + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder conn = connections[i]; + if (conn != null) { + if (i != 0) { + buf.append(","); + } + buf.append("\n\t{\n\tID:"); + buf.append(System.identityHashCode(conn.getConnection())); + PreparedStatementPool pool = conn.getStatementPool(); + + buf.append(", \n\tpoolStatements:["); + + int entryIndex = 0; + try { + for (Map.Entry entry : pool.getMap().entrySet()) { + if (entryIndex != 0) { + buf.append(","); + } + buf.append("\n\t\t{hitCount:"); + buf.append(entry.getValue().getHitCount()); + buf.append(",sql:\""); + buf.append(entry.getKey().getSql()); + buf.append("\""); + buf.append("\t}"); + + entryIndex++; + } + } catch (ConcurrentModificationException e) { + // skip .. + } + + buf.append("\n\t\t]"); + + buf.append("\n\t}"); + } + } + buf.append("\n]"); + } + + return buf.toString(); + } + + public List> getPoolingConnectionInfo() { + List> list = new ArrayList>(); + lock.lock(); + try { + for (int i = 0; i < poolingCount; ++i) { + DruidConnectionHolder connHolder = connections[i]; + Connection conn = connHolder.getConnection(); + + Map map = new LinkedHashMap(); + map.put("id", System.identityHashCode(conn)); + map.put("useCount", connHolder.getUseCount()); + if (connHolder.getLastActiveTimeMillis() > 0) { + map.put("lastActiveTime", new Date(connHolder.getLastActiveTimeMillis())); + } + map.put("connectTime", new Date(connHolder.getTimeMillis())); + map.put("holdability", connHolder.getUnderlyingHoldability()); + map.put("transactionIsolation", connHolder.getUnderlyingTransactionIsolation()); + map.put("autoCommit", connHolder.isUnderlyingAutoCommit()); + map.put("readoOnly", connHolder.isUnderlyingReadOnly()); + + if (connHolder.isPoolPreparedStatements()) { + List> stmtCache = new ArrayList>(); + PreparedStatementPool stmtPool = connHolder.getStatementPool(); + for (PreparedStatementHolder stmtHolder : stmtPool.getMap().values()) { + Map stmtInfo = new LinkedHashMap(); + + stmtInfo.put("sql", stmtHolder.getKey().getSql()); + stmtInfo.put("defaultRowPretch", stmtHolder.getDefaultRowPretch()); + stmtInfo.put("rowPrefetch", stmtHolder.getRowPrefetch()); + stmtInfo.put("hitCount", stmtHolder.getHitCount()); + + stmtCache.add(stmtInfo); + } + + map.put("pscache", stmtCache); + } + + list.add(map); + } + } finally { + lock.unlock(); + } + return list; + } + + public void logTransaction(TransactionInfo info) { + long transactionMillis = info.getEndTimeMillis() - info.getStartTimeMillis(); + if (transactionThresholdMillis > 0 && transactionMillis > transactionThresholdMillis) { + StringBuilder buf = new StringBuilder(); + buf.append("long time transaction, take "); + buf.append(transactionMillis); + buf.append(" ms : "); + for (String sql : info.getSqlList()) { + buf.append(sql); + buf.append(";"); + } + LOG.error(buf.toString(), new TransactionTimeoutException()); + } + } + + @Override + public String getVersion() { + return VERSION.getVersionNumber(); + } + + @Override + public JdbcDataSourceStat getDataSourceStat() { + return dataSourceStat; + } + + public Object clone() throws CloneNotSupportedException { + return cloneDruidDataSource(); + } + + public DruidDataSource cloneDruidDataSource() { + DruidDataSource x = new DruidDataSource(); + + cloneTo(x); + + return x; + } + + public Map getStatDataForMBean() { + try { + Map map = new HashMap(); + + // 0 - 4 + map.put("Name", this.getName()); + map.put("URL", this.getUrl()); + map.put("CreateCount", this.getCreateCount()); + map.put("DestroyCount", this.getDestroyCount()); + map.put("ConnectCount", this.getConnectCount()); + + // 5 - 9 + map.put("CloseCount", this.getCloseCount()); + map.put("ActiveCount", this.getActivePeak()); + map.put("PoolingCount", this.getPoolingCount()); + map.put("LockQueueLength", this.getLockQueueLength()); + map.put("WaitThreadCount", this.getNotEmptyWaitThreadPeak()); + + // 10 - 14 + map.put("InitialSize", this.getInitialSize()); + map.put("MaxActive", this.getMaxActive()); + map.put("MinIdle", this.getMinIdle()); + map.put("PoolPreparedStatements", this.isPoolPreparedStatements()); + map.put("TestOnBorrow", this.isTestOnBorrow()); + + // 15 - 19 + map.put("TestOnReturn", this.isTestOnReturn()); + map.put("MinEvictableIdleTimeMillis", this.getMinEvictableIdleTimeMillis()); + map.put("ConnectErrorCount", this.getConnectErrorCount()); + map.put("CreateTimespanMillis", this.getCreateTimespanMillis()); + map.put("DbType", this.getDbType()); + + // 20 - 24 + map.put("ValidationQuery", this.getValidationQuery()); + map.put("ValidationQueryTimeout", this.getValidationQueryTimeout()); + map.put("DriverClassName", this.getDriverClassName()); + map.put("Username", this.getUsername()); + map.put("RemoveAbandonedCount", this.getRemoveAbandonedCount()); + + // 25 - 29 + map.put("NotEmptyWaitCount", this.getNotEmptyWaitCount()); + map.put("NotEmptyWaitNanos", this.getNotEmptyWaitNanos()); + map.put("ErrorCount", this.getErrorCount()); + map.put("ReusePreparedStatementCount", this.getCachedPreparedStatementHitCount()); + map.put("StartTransactionCount", this.getStartTransactionCount()); + + // 30 - 34 + map.put("CommitCount", this.getCommitCount()); + map.put("RollbackCount", this.getRollbackCount()); + map.put("LastError", JMXUtils.getErrorCompositeData(this.getLastError())); + map.put("LastCreateError", JMXUtils.getErrorCompositeData(this.getLastCreateError())); + map.put("PreparedStatementCacheDeleteCount", this.getCachedPreparedStatementDeleteCount()); + + // 35 - 39 + map.put("PreparedStatementCacheAccessCount", this.getCachedPreparedStatementAccessCount()); + map.put("PreparedStatementCacheMissCount", this.getCachedPreparedStatementMissCount()); + map.put("PreparedStatementCacheHitCount", this.getCachedPreparedStatementHitCount()); + map.put("PreparedStatementCacheCurrentCount", this.getCachedPreparedStatementCount()); + map.put("Version", this.getVersion()); + + // 40 - + map.put("LastErrorTime", this.getLastErrorTime()); + map.put("LastCreateErrorTime", this.getLastCreateErrorTime()); + map.put("CreateErrorCount", this.getCreateErrorCount()); + map.put("DiscardCount", this.getDiscardCount()); + + return map; + } catch (JMException ex) { + throw new IllegalStateException("getStatData error", ex); + } + } + + public Map getStatData() { + final int activeCount; + final int activePeak; + final Date activePeakTime; + + final int poolingCount; + final int poolingPeak; + final Date poolingPeakTime; + + final long connectCount; + final long closeCount; + + lock.lock(); + try { + poolingCount = this.poolingCount; + poolingPeak = this.poolingPeak; + poolingPeakTime = this.getPoolingPeakTime(); + + activeCount = this.activeCount; + activePeak = this.activePeak; + activePeakTime = this.getActivePeakTime(); + + connectCount = this.connectCount; + closeCount = this.closeCount; + } finally { + lock.unlock(); + } + Map dataMap = new LinkedHashMap(); + + dataMap.put("Identity", System.identityHashCode(this)); + dataMap.put("Name", this.getName()); + dataMap.put("DbType", this.getDbType()); + dataMap.put("DriverClassName", this.getDriverClassName()); + + dataMap.put("URL", this.getUrl()); + dataMap.put("UserName", this.getUsername()); + dataMap.put("FilterClassNames", this.getFilterClassNames()); + + dataMap.put("WaitThreadCount", this.getWaitThreadCount()); + dataMap.put("NotEmptyWaitCount", this.getNotEmptyWaitCount()); + dataMap.put("NotEmptyWaitMillis", this.getNotEmptyWaitMillis()); + + dataMap.put("PoolingCount", poolingCount); + dataMap.put("PoolingPeak", poolingPeak); + dataMap.put("PoolingPeakTime", poolingPeakTime); + + dataMap.put("ActiveCount", activeCount); + dataMap.put("ActivePeak", activePeak); + dataMap.put("ActivePeakTime", activePeakTime); + + dataMap.put("InitialSize", this.getInitialSize()); + dataMap.put("MinIdle", this.getMinIdle()); + dataMap.put("MaxActive", this.getMaxActive()); + + dataMap.put("QueryTimeout", this.getQueryTimeout()); + dataMap.put("TransactionQueryTimeout", this.getTransactionQueryTimeout()); + dataMap.put("LoginTimeout", this.getLoginTimeout()); + dataMap.put("ValidConnectionCheckerClassName", this.getValidConnectionCheckerClassName()); + dataMap.put("ExceptionSorterClassName", this.getExceptionSorterClassName()); + + dataMap.put("TestOnBorrow", this.isTestOnBorrow()); + dataMap.put("TestOnReturn", this.isTestOnReturn()); + dataMap.put("TestWhileIdle", this.isTestWhileIdle()); + + dataMap.put("DefaultAutoCommit", this.isDefaultAutoCommit()); + dataMap.put("DefaultReadOnly", this.getDefaultReadOnly()); + dataMap.put("DefaultTransactionIsolation", this.getDefaultTransactionIsolation()); + + dataMap.put("LogicConnectCount", connectCount); + dataMap.put("LogicCloseCount", closeCount); + dataMap.put("LogicConnectErrorCount", this.getConnectErrorCount()); + + dataMap.put("PhysicalConnectCount", this.getCreateCount()); + dataMap.put("PhysicalCloseCount", this.getDestroyCount()); + dataMap.put("PhysicalConnectErrorCount", this.getCreateErrorCount()); + + dataMap.put("ExecuteCount", this.getExecuteCount()); + dataMap.put("ErrorCount", this.getErrorCount()); + dataMap.put("CommitCount", this.getCommitCount()); + dataMap.put("RollbackCount", this.getRollbackCount()); + + dataMap.put("PSCacheAccessCount", this.getCachedPreparedStatementAccessCount()); + dataMap.put("PSCacheHitCount", this.getCachedPreparedStatementHitCount()); + dataMap.put("PSCacheMissCount", this.getCachedPreparedStatementMissCount()); + + dataMap.put("StartTransactionCount", this.getStartTransactionCount()); + dataMap.put("TransactionHistogram", this.getTransactionHistogramValues()); + + dataMap.put("ConnectionHoldTimeHistogram", this.getDataSourceStat().getConnectionHoldHistogram().toArray()); + dataMap.put("RemoveAbandoned", this.isRemoveAbandoned()); + dataMap.put("ClobOpenCount", this.getDataSourceStat().getClobOpenCount()); + dataMap.put("BlobOpenCount", this.getDataSourceStat().getBlobOpenCount()); + + return dataMap; + } + + public JdbcSqlStat getSqlStat(int sqlId) { + return this.getDataSourceStat().getSqlStat(sqlId); + } + + public JdbcSqlStat getSqlStat(long sqlId) { + return this.getDataSourceStat().getSqlStat(sqlId); + } + + public Map getSqlStatMap() { + return this.getDataSourceStat().getSqlStatMap(); + } + + public Map getWallStatMap() { + WallProviderStatValue wallStatValue = getWallStatValue(false); + + if (wallStatValue != null) { + return wallStatValue.toMap(); + } + + return null; + } + + public WallProviderStatValue getWallStatValue(boolean reset) { + for (Filter filter : this.filters) { + if (filter instanceof WallFilter) { + WallFilter wallFilter = (WallFilter) filter; + return wallFilter.getProvider().getStatValue(reset); + } + } + + return null; + } + + public Lock getLock() { + return lock; + } + + @Override + public boolean isWrapperFor(Class iface) { + for (Filter filter : this.filters) { + if (filter.isWrapperFor(iface)) { + return true; + } + } + + if (this.statLogger != null + && (this.statLogger.getClass() == iface || DruidDataSourceStatLogger.class == iface)) { + return true; + } + + return super.isWrapperFor(iface); + } + + @SuppressWarnings("unchecked") + public T unwrap(Class iface) { + for (Filter filter : this.filters) { + if (filter.isWrapperFor(iface)) { + return (T) filter; + } + } + + if (this.statLogger != null + && (this.statLogger.getClass() == iface || DruidDataSourceStatLogger.class == iface)) { + return (T) statLogger; + } + + return super.unwrap(iface); + } + + public boolean isLogDifferentThread() { + return logDifferentThread; + } + + public void setLogDifferentThread(boolean logDifferentThread) { + this.logDifferentThread = logDifferentThread; + } + + public DruidPooledConnection tryGetConnection() throws SQLException { + if (poolingCount == 0) { + return null; + } + return getConnection(); + } + + @Override + public int fill() throws SQLException { + return this.fill(this.maxActive); + } + + @Override + public int fill(int toCount) throws SQLException { + if (closed) { + throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis)); + } + + if (toCount < 0) { + throw new IllegalArgumentException("toCount can't not be less than zero"); + } + + init(); + + if (toCount > this.maxActive) { + toCount = this.maxActive; + } + + int fillCount = 0; + for (;;) { + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + connectErrorCount.incrementAndGet(); + throw new SQLException("interrupt", e); + } + + boolean fillable = this.isFillable(toCount); + + lock.unlock(); + + if (!fillable) { + break; + } + + DruidConnectionHolder holder; + try { + Connection conn = createPhysicalConnection(); + holder = new DruidConnectionHolder(this, conn); + } catch (SQLException e) { + LOG.error("fill connection error", e); + connectErrorCount.incrementAndGet(); + throw e; + } + + try { + lock.lockInterruptibly(); + } catch (InterruptedException e) { + connectErrorCount.incrementAndGet(); + throw new SQLException("interrupt", e); + } + + try { + if (!this.isFillable(toCount)) { + JdbcUtils.close(holder.getConnection()); + break; + } + this.putLast(holder, System.currentTimeMillis()); + fillCount++; + } finally { + lock.unlock(); + } + } + + if (LOG.isInfoEnabled()) { + LOG.info("fill " + fillCount + " connections"); + } + + return fillCount; + } + + private boolean isFillable(int toCount) { + int currentCount = this.poolingCount + this.activeCount; + if (currentCount >= toCount || currentCount >= this.maxActive) { + return false; + } else { + return true; + } + } + + public boolean isFull() { + lock.lock(); + try { + return this.poolingCount + this.activeCount >= this.maxActive; + } finally { + lock.unlock(); + } + } +} diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSourceMBean.java b/src/main/java/com/alibaba/druid/pool/DruidDataSourceMBean.java index 3af9d08463..11ded24669 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSourceMBean.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSourceMBean.java @@ -81,4 +81,8 @@ public interface DruidDataSourceMBean extends DruidAbstractDataSourceMBean { void setTimeBetweenLogStatsMillis(long timeBetweenLogStatsMillis); void setConnectionProperties(String connectionProperties); + + int fill() throws SQLException; + + int fill(int toCount) throws SQLException; } diff --git a/src/main/java/com/alibaba/druid/sql/PagerUtils.java b/src/main/java/com/alibaba/druid/sql/PagerUtils.java index 227f7aec87..24067727ff 100644 --- a/src/main/java/com/alibaba/druid/sql/PagerUtils.java +++ b/src/main/java/com/alibaba/druid/sql/PagerUtils.java @@ -38,7 +38,6 @@ import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; -import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock; import com.alibaba.druid.sql.dialect.postgresql.ast.stmt.PGSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; @@ -367,13 +366,8 @@ public class PagerUtils { } private static SQLSelectItem createCountItem(String dbType) { - SQLAggregateExpr countExpr; + SQLAggregateExpr countExpr = new SQLAggregateExpr("COUNT"); - if (JdbcConstants.ORACLE.equals(dbType)) { - countExpr = new OracleAggregateExpr("COUNT"); - } else { - countExpr = new SQLAggregateExpr("COUNT"); - } countExpr.getArguments().add(new SQLAllColumnExpr()); SQLSelectItem countItem = new SQLSelectItem(countExpr); diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLOrderBy.java b/src/main/java/com/alibaba/druid/sql/ast/SQLOrderBy.java index be5c893e27..a453b1187a 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/SQLOrderBy.java +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLOrderBy.java @@ -74,4 +74,5 @@ public class SQLOrderBy extends SQLObjectImpl { return true; } + } diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java index 89c1fd6a0f..38fe123edf 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java @@ -21,6 +21,7 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLExprImpl; +import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLOver; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -31,9 +32,10 @@ public class SQLAggregateExpr extends SQLExprImpl implements Serializable { protected Option option; protected final List arguments = new ArrayList(); protected SQLOver over; + protected SQLOrderBy withinGroup; + protected boolean ignoreNulls = false; public SQLAggregateExpr(String methodName){ - this.methodName = methodName; } @@ -50,6 +52,18 @@ public class SQLAggregateExpr extends SQLExprImpl implements Serializable { this.methodName = methodName; } + public SQLOrderBy getWithinGroup() { + return withinGroup; + } + + public void setWithinGroup(SQLOrderBy withinGroup) { + if (withinGroup != null) { + withinGroup.setParent(this); + } + + this.withinGroup = withinGroup; + } + public Option getOption() { return this.option; } @@ -69,6 +83,15 @@ public class SQLAggregateExpr extends SQLExprImpl implements Serializable { public void setOver(SQLOver over) { this.over = over; } + + public boolean isIgnoreNulls() { + return this.ignoreNulls; + } + + public void setIgnoreNulls(boolean ignoreNulls) { + this.ignoreNulls = ignoreNulls; + } + @Override protected void accept0(SQLASTVisitor visitor) { diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBooleanExpr.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBooleanExpr.java new file mode 100644 index 0000000000..903680722c --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBooleanExpr.java @@ -0,0 +1,80 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.sql.ast.expr; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLExprImpl; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class SQLBooleanExpr extends SQLExprImpl implements SQLExpr, SQLLiteralExpr { + + private boolean value; + + public SQLBooleanExpr(){ + + } + + public SQLBooleanExpr(boolean value){ + this.value = value; + } + + public boolean getValue() { + return value; + } + + public void setValue(boolean value) { + this.value = value; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + visitor.visit(this); + + visitor.endVisit(this); + } + + public void output(StringBuffer buf) { + buf.append("x"); + buf.append(value ? "TRUE" : "FALSE"); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (value ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + SQLBooleanExpr other = (SQLBooleanExpr) obj; + if (value != other.value) { + return false; + } + return true; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectOrderByItem.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectOrderByItem.java index 622bc800ce..723594d1e3 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectOrderByItem.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLSelectOrderByItem.java @@ -25,6 +25,7 @@ public class SQLSelectOrderByItem extends SQLObjectImpl { protected SQLExpr expr; protected String collate; protected SQLOrderingSpecification type; + protected NullsOrderType nullsOrderType; public SQLSelectOrderByItem(){ @@ -60,6 +61,14 @@ public class SQLSelectOrderByItem extends SQLObjectImpl { public void setType(SQLOrderingSpecification type) { this.type = type; } + + public NullsOrderType getNullsOrderType() { + return this.nullsOrderType; + } + + public void setNullsOrderType(NullsOrderType nullsOrderType) { + this.nullsOrderType = nullsOrderType; + } protected void accept0(SQLASTVisitor visitor) { if (visitor.visit(this)) { @@ -95,4 +104,19 @@ public class SQLSelectOrderByItem extends SQLObjectImpl { return true; } + public static enum NullsOrderType { + NullsFirst, NullsLast; + + public String toFormalString() { + if (NullsFirst.equals(this)) { + return "NULLS FIRST"; + } + + if (NullsLast.equals(this)) { + return "NULLS LAST"; + } + + throw new IllegalArgumentException(); + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQueryTableSource.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQueryTableSource.java new file mode 100644 index 0000000000..b9d9472a10 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQueryTableSource.java @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.sql.ast.statement; + +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class SQLUnionQueryTableSource extends SQLTableSourceImpl { + + private SQLUnionQuery union; + + public SQLUnionQueryTableSource(){ + + } + + public SQLUnionQueryTableSource(String alias){ + super(alias); + } + + public SQLUnionQueryTableSource(SQLUnionQuery union, String alias){ + super(alias); + this.setUnion(union); + } + + public SQLUnionQueryTableSource(SQLUnionQuery union){ + this.setUnion(union); + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, union); + } + visitor.endVisit(this); + } + + public void output(StringBuffer buf) { + buf.append("("); + this.union.output(buf); + buf.append(")"); + } + + public SQLUnionQuery getUnion() { + return union; + } + + public void setUnion(SQLUnionQuery union) { + if (union != null) { + union.setParent(this); + } + this.union = union; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlBooleanExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlBooleanExpr.java index ba26add238..af19c47b03 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlBooleanExpr.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlBooleanExpr.java @@ -15,70 +15,11 @@ */ package com.alibaba.druid.sql.dialect.mysql.ast.expr; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; -import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; -import com.alibaba.druid.sql.visitor.SQLASTVisitor; -public class MySqlBooleanExpr extends MySqlExprImpl implements MySqlExpr, SQLLiteralExpr { +public class MySqlBooleanExpr extends SQLBooleanExpr implements MySqlExpr, SQLLiteralExpr { - private boolean value; - - public MySqlBooleanExpr(){ - - } - - public MySqlBooleanExpr(boolean value){ - this.value = value; - } - - public boolean getValue() { - return value; - } - - public void setValue(boolean value) { - this.value = value; - } - - @Override - protected void accept0(SQLASTVisitor visitor) { - this.accept0((MySqlASTVisitor) visitor); - } - - public void accept0(MySqlASTVisitor visitor) { - visitor.visit(this); - - visitor.endVisit(this); - } - - public void output(StringBuffer buf) { - buf.append("x"); - buf.append(value ? "TRUE" : "FALSE"); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (value ? 1231 : 1237); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - MySqlBooleanExpr other = (MySqlBooleanExpr) obj; - if (value != other.value) { - return false; - } - return true; - } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlCreateTableParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlCreateTableParser.java index 0b6ed3783a..6d9a818064 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlCreateTableParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlCreateTableParser.java @@ -493,7 +493,7 @@ public class MySqlCreateTableParser extends SQLCreateTableParser { MySqlPartitioningDef.InValues values = new MySqlPartitioningDef.InValues(); accept(Token.LPAREN); - this.exprParser.exprList(values.getItems()); + this.exprParser.exprList(values.getItems(), values); accept(Token.RPAREN); partitionDef.setValues(values); } else { @@ -503,7 +503,7 @@ public class MySqlCreateTableParser extends SQLCreateTableParser { MySqlPartitioningDef.LessThanValues values = new MySqlPartitioningDef.LessThanValues(); accept(Token.LPAREN); - this.exprParser.exprList(values.getItems()); + this.exprParser.exprList(values.getItems(), values); accept(Token.RPAREN); partitionDef.setValues(values); } @@ -550,6 +550,10 @@ public class MySqlCreateTableParser extends SQLCreateTableParser { if (lexer.token() == (Token.ON)) { throw new ParserException("TODO"); } + + if (lexer.token() == (Token.AS)) { + lexer.nextToken(); + } if (lexer.token() == (Token.SELECT)) { SQLSelect query = new MySqlSelectParser(this.exprParser).select(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java index e2b7dd26c5..deef36e75f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java @@ -36,7 +36,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; @@ -138,12 +137,6 @@ public class MySqlExprParser extends SQLExprParser { } switch (tok) { - case TRUE: - lexer.nextToken(); - return primaryRest(new MySqlBooleanExpr(true)); - case FALSE: - lexer.nextToken(); - return primaryRest(new MySqlBooleanExpr(false)); case LITERAL_ALIAS: String aliasValue = lexer.stringVal(); lexer.nextToken(); @@ -234,7 +227,7 @@ public class MySqlExprParser extends SQLExprParser { String chars = lexer.stringVal(); concat.addParameter(new SQLCharExpr(chars)); lexer.nextToken(); - } while (lexer.token() == Token.LITERAL_CHARS); + } while (lexer.token() == Token.LITERAL_CHARS || lexer.token() == Token.LITERAL_ALIAS); expr = concat; } } else if (lexer.token() == Token.IDENTIFIER) { @@ -394,7 +387,7 @@ public class MySqlExprParser extends SQLExprParser { if (lexer.token() == Token.RPAREN) { lexer.nextToken(); } else { - exprList(matchAgainstExpr.getColumns()); + exprList(matchAgainstExpr.getColumns(), matchAgainstExpr); accept(Token.RPAREN); } @@ -490,6 +483,11 @@ public class MySqlExprParser extends SQLExprParser { lexer.nextToken(); return userName; } + + if(lexer.token() == Token.ERROR) { + throw new ParserException("syntax error, token: " + lexer.token() + " " + lexer.stringVal() + ", pos : " + + lexer.pos()); + } return super.primaryRest(expr); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java index 9944ab2acd..8371dbe9bf 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java @@ -105,6 +105,7 @@ public class MySqlLexer extends Lexer { stringVal = subString(mark, bufPos); token = Token.LINE_COMMENT; + hasComment = true; if (commentHandler != null && commentHandler.handle(lastToken, stringVal)) { return; @@ -440,6 +441,7 @@ public class MySqlLexer extends Lexer { } else { stringVal = subString(mark, bufPos); token = Token.MULTI_LINE_COMMENT; + hasComment = true; } if (commentHandler != null && commentHandler.handle(lastToken, stringVal)) { @@ -481,6 +483,7 @@ public class MySqlLexer extends Lexer { stringVal = subString(mark, bufPos + 1); token = Token.LINE_COMMENT; + hasComment = true; if (commentHandler != null && commentHandler.handle(lastToken, stringVal)) { return; diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java index 0e3a1244bd..9dd503b1c8 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java @@ -1582,7 +1582,7 @@ public class MySqlStatementParser extends SQLStatementParser { SQLQueryExpr queryExpr = (SQLQueryExpr) this.exprParser.expr(); stmt.setQuery(queryExpr); } else { - this.exprParser.exprList(stmt.getColumns()); + this.exprParser.exprList(stmt.getColumns(), stmt); } accept(Token.RPAREN); } @@ -1807,7 +1807,7 @@ public class MySqlStatementParser extends SQLStatementParser { if (identifierEquals("USING")) { lexer.nextToken(); - exprParser.exprList(stmt.getParameters()); + exprParser.exprList(stmt.getParameters(), stmt); } return stmt; @@ -1861,7 +1861,7 @@ public class MySqlStatementParser extends SQLStatementParser { select.setParent(insertStatement); insertStatement.setQuery(select); } else { - this.exprParser.exprList(insertStatement.getColumns()); + this.exprParser.exprList(insertStatement.getColumns(), insertStatement); columnSize = insertStatement.getColumns().size(); } accept(Token.RPAREN); @@ -1912,7 +1912,7 @@ public class MySqlStatementParser extends SQLStatementParser { accept(Token.KEY); accept(Token.UPDATE); - exprParser.exprList(insertStatement.getDuplicateKeyUpdate()); + exprParser.exprList(insertStatement.getDuplicateKeyUpdate(), insertStatement); } return insertStatement; @@ -1957,6 +1957,10 @@ public class MySqlStatementParser extends SQLStatementParser { break; } else { expr = this.exprParser.primaryRest(expr); + if(lexer.token() != Token.COMMA && lexer.token() != Token.RPAREN) { + expr = this.exprParser.exprRest(expr); + } + valueExprList.add(expr); if (lexer.token() == Token.COMMA) { lexer.nextToken(); @@ -2520,7 +2524,7 @@ public class MySqlStatementParser extends SQLStatementParser { if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); SQLListExpr list = new SQLListExpr(); - this.exprParser.exprList(list.getItems()); + this.exprParser.exprList(list.getItems(), list); accept(Token.RPAREN); item.setColumn(list); } else { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySql2OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySql2OracleOutputVisitor.java index 5b33cca785..f020f54a26 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySql2OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySql2OracleOutputVisitor.java @@ -15,7 +15,6 @@ */ package com.alibaba.druid.sql.dialect.mysql.visitor; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; public class MySql2OracleOutputVisitor extends MySqlOutputVisitor { @@ -23,11 +22,5 @@ public class MySql2OracleOutputVisitor extends MySqlOutputVisitor { super(appender); } - public boolean visit(MySqlBooleanExpr x) { - return true; - } - - public void endVisit(MySqlBooleanExpr x) { - } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java index f42ae9138d..e9f5e338fe 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java @@ -23,7 +23,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; @@ -123,11 +122,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public interface MySqlASTVisitor extends SQLASTVisitor { - - boolean visit(MySqlBooleanExpr x); - - void endVisit(MySqlBooleanExpr x); - boolean visit(MySqlSelectQueryBlock.Limit x); void endVisit(MySqlSelectQueryBlock.Limit x); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java index fa166fbd0f..67da8b1226 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java @@ -23,7 +23,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; @@ -128,17 +127,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter; public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySqlASTVisitor { - - @Override - public boolean visit(MySqlBooleanExpr x) { - return true; - } - - @Override - public void endVisit(MySqlBooleanExpr x) { - - } - @Override public boolean visit(Limit x) { return true; diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlEvalVisitorImpl.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlEvalVisitorImpl.java index c70e199db1..3629dbb837 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlEvalVisitorImpl.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlEvalVisitorImpl.java @@ -22,6 +22,7 @@ import java.util.Map; import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLHexExpr; @@ -35,7 +36,6 @@ import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLUnaryExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.visitor.SQLEvalVisitor; import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; import com.alibaba.druid.sql.visitor.functions.Function; @@ -135,12 +135,6 @@ public class MySqlEvalVisitorImpl extends MySqlASTVisitorAdapter implements SQLE return SQLEvalVisitorUtils.visit(this, x); } - @Override - public boolean visit(MySqlBooleanExpr x) { - x.getAttributes().put(EVAL_VALUE, x.getValue()); - return false; - } - public boolean isMarkVariantIndex() { return markVariantIndex; } @@ -167,4 +161,10 @@ public class MySqlEvalVisitorImpl extends MySqlASTVisitorAdapter implements SQLE public void unregisterFunction(String funcName) { functions.remove(funcName); } + + @Override + public boolean visit(SQLBooleanExpr x) { + x.getAttributes().put(EVAL_VALUE, x.getValue()); + return false; + } } 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 dd4f8fb289..e86ded6ce4 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 @@ -45,7 +45,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; @@ -156,22 +155,13 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV super(appender); } - public boolean visit(MySqlBooleanExpr x) { - print(x.getValue() ? "true" : "false"); - - return false; - } - - public void endVisit(MySqlBooleanExpr x) { - } - @Override public boolean visit(SQLSelectQueryBlock select) { if (select instanceof MySqlSelectQueryBlock) { return visit((MySqlSelectQueryBlock) select); } - return false; + return super.visit(select); } public boolean visit(MySqlSelectQueryBlock x) { @@ -383,7 +373,6 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV if (x.getCharSetName() != null) { print(" CHARACTER SET "); print(x.getCharSetName()); - if (x.getCollate() != null) { print(" COLLATE "); print(x.getCollate()); @@ -419,7 +408,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV print(x.getIndexType()); } - print("("); + print(" ("); for (int i = 0, size = x.getColumns().size(); i < size; ++i) { if (i != 0) { print(", "); 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 68a3c04488..520327e6fa 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 @@ -22,6 +22,7 @@ import java.util.Properties; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLHexExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLInListExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; @@ -31,6 +32,7 @@ import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; import com.alibaba.druid.sql.visitor.ParameterizedVisitor; @@ -179,6 +181,22 @@ public class MySqlParameterizedOutputVisitor extends MySqlOutputVisitor implemen return ParameterizedOutputVisitorUtils.visit(this, x); } + + public boolean visit(SQLHexExpr x) { + if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) { + return super.visit(x); + } + + return ParameterizedOutputVisitorUtils.visit(this, x); + } + + public boolean visit(MySqlCharExpr x) { + if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) { + return super.visit(x); + } + + return ParameterizedOutputVisitorUtils.visit(this, x); + } protected void printValuesList(MySqlInsertStatement x) { List valuesList = x.getValuesList(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java index 68b9d0265a..5f3eeabdd5 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java @@ -37,7 +37,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; @@ -224,17 +223,6 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS return false; } - @Override - public boolean visit(MySqlBooleanExpr x) { - - return true; - } - - @Override - public void endVisit(MySqlBooleanExpr x) { - - } - @Override public boolean visit(Limit x) { @@ -249,7 +237,7 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS @Override public boolean visit(MySqlTableIndex x) { - return true; + return false; } @Override diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java index eaf42c112f..68f179def7 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java @@ -92,6 +92,7 @@ public class OdpsLexer extends Lexer { } else { stringVal = subString(mark, bufPos); token = Token.MULTI_LINE_COMMENT; + hasComment = true; } if (token != Token.HINT && !isAllowComment()) { @@ -134,6 +135,7 @@ public class OdpsLexer extends Lexer { stringVal = subString(mark + 1, bufPos); token = Token.LINE_COMMENT; + hasComment = true; return; } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAggregateExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAggregateExpr.java deleted file mode 100644 index 6660323808..0000000000 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAggregateExpr.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 1999-2011 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.druid.sql.dialect.oracle.ast.expr; - -import java.io.Serializable; - -import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; -import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; -import com.alibaba.druid.sql.visitor.SQLASTVisitor; - -public class OracleAggregateExpr extends SQLAggregateExpr implements Serializable, OracleExpr { - - private static final long serialVersionUID = 1L; - private boolean ignoreNulls = false; - - public boolean isUnique() { - return Option.UNIQUE.equals(this.getOption()); - } - - public void setUnique(boolean unique) { - this.setOption(Option.UNIQUE); - } - - public boolean isIgnoreNulls() { - return this.ignoreNulls; - } - - public void setIgnoreNulls(boolean ignoreNulls) { - this.ignoreNulls = ignoreNulls; - } - - public OracleAggregateExpr(String methodName){ - super(methodName); - } - - public OracleAggregateExpr(String methodName, Option option){ - super(methodName, option); - } - - public void output(StringBuffer buf) { - buf.append(this.methodName); - buf.append("("); - int i = 0; - for (int size = this.arguments.size(); i < size; ++i) { - ((SQLExpr) this.arguments.get(i)).output(buf); - } - buf.append(")"); - - if (this.over != null) { - buf.append(" "); - this.over.output(buf); - } - } - - @Override - protected void accept0(SQLASTVisitor visitor) { - if (visitor instanceof OracleASTVisitor) { - this.accept0((OracleASTVisitor) visitor); - } else { - if (visitor.visit(this)) { - acceptChild(visitor, this.arguments); - acceptChild(visitor, this.over); - } - visitor.endVisit(this); - } - } - - public void accept0(OracleASTVisitor visitor) { - if (visitor.visit(this)) { - acceptChild(visitor, this.arguments); - acceptChild(visitor, this.over); - } - visitor.endVisit(this); - } -} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleOrderByItem.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleOrderByItem.java index 82e03d043a..a00d47ea6a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleOrderByItem.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleOrderByItem.java @@ -22,20 +22,10 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class OracleOrderByItem extends SQLSelectOrderByItem { - private NullsOrderType nullsOrderType; - public OracleOrderByItem(){ } - public NullsOrderType getNullsOrderType() { - return this.nullsOrderType; - } - - public void setNullsOrderType(NullsOrderType nullsOrderType) { - this.nullsOrderType = nullsOrderType; - } - protected void accept0(SQLASTVisitor visitor) { if (visitor instanceof OracleASTVisitor) { accept0((OracleASTVisitor) visitor); @@ -66,20 +56,4 @@ public class OracleOrderByItem extends SQLSelectOrderByItem { } } - public static enum NullsOrderType { - NullsFirst, NullsLast; - - public String toFormalString() { - if (NullsFirst.equals(this)) { - return "NULLS FIRST"; - } - - if (NullsLast.equals(this)) { - return "NULLS LAST"; - } - - throw new IllegalArgumentException(); - } - } - } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java index d4ce172654..7089bd4071 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java @@ -21,6 +21,7 @@ import com.alibaba.druid.sql.ast.SQLDataType; import com.alibaba.druid.sql.ast.SQLDataTypeImpl; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; @@ -47,7 +48,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleLobStorageClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleStorageClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleStorageClause.FlashCacheType; -import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalytic; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalyticWindowing; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleArgumentExpr; @@ -106,6 +106,7 @@ public class OracleExprParser extends SQLExprParser { "LAG", // "LAST", // "LAST_VALUE", // + "LISTAGG", "LEAD", // "MAX", // "MIN", // @@ -744,23 +745,23 @@ public class OracleExprParser extends SQLExprParser { return null; } - protected OracleAggregateExpr parseAggregateExpr(String methodName) { + protected SQLAggregateExpr parseAggregateExpr(String methodName) { methodName = methodName.toUpperCase(); - OracleAggregateExpr aggregateExpr; + SQLAggregateExpr aggregateExpr; if (lexer.token() == Token.UNIQUE) { - aggregateExpr = new OracleAggregateExpr(methodName, SQLAggregateExpr.Option.UNIQUE); + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateExpr.Option.UNIQUE); lexer.nextToken(); } else if (lexer.token() == (Token.ALL)) { - aggregateExpr = new OracleAggregateExpr(methodName, SQLAggregateExpr.Option.ALL); + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateExpr.Option.ALL); lexer.nextToken(); } else if (lexer.token() == (Token.DISTINCT)) { - aggregateExpr = new OracleAggregateExpr(methodName, SQLAggregateExpr.Option.DISTINCT); + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateExpr.Option.DISTINCT); lexer.nextToken(); } else { - aggregateExpr = new OracleAggregateExpr(methodName); + aggregateExpr = new SQLAggregateExpr(methodName); } - exprList(aggregateExpr.getArguments()); + exprList(aggregateExpr.getArguments(), aggregateExpr); if (lexer.stringVal().equalsIgnoreCase("IGNORE")) { lexer.nextToken(); @@ -769,6 +770,15 @@ public class OracleExprParser extends SQLExprParser { } accept(Token.RPAREN); + + if (identifierEquals("WITHIN")) { + lexer.nextToken(); + accept(Token.GROUP); + accept(Token.LPAREN); + SQLOrderBy withinGroup = this.parseOrderBy(); + aggregateExpr.setWithinGroup(withinGroup); + accept(Token.RPAREN); + } if (lexer.token() == Token.OVER) { OracleAnalytic over = new OracleAnalytic(); @@ -782,10 +792,10 @@ public class OracleExprParser extends SQLExprParser { if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); - exprList(over.getPartitionBy()); + exprList(over.getPartitionBy(), over); accept(Token.RPAREN); } else { - exprList(over.getPartitionBy()); + exprList(over.getPartitionBy(), over); } } @@ -1024,7 +1034,7 @@ public class OracleExprParser extends SQLExprParser { OraclePrimaryKey primaryKey = new OraclePrimaryKey(); accept(Token.LPAREN); - exprList(primaryKey.getColumns()); + exprList(primaryKey.getColumns(), primaryKey); accept(Token.RPAREN); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java index 3d3f6321ca..2352d2fdfb 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java @@ -230,6 +230,7 @@ public class OracleLexer extends Lexer { } else { stringVal = subString(mark, bufPos); token = Token.MULTI_LINE_COMMENT; + hasComment = true; } if (token != Token.HINT && !isAllowComment()) { @@ -272,6 +273,7 @@ public class OracleLexer extends Lexer { stringVal = subString(mark + 1, bufPos); token = Token.LINE_COMMENT; + hasComment = true; return; } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java index 541a7ace25..2c52741226 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java @@ -19,6 +19,7 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLSetQuantifier; +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; @@ -52,7 +53,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleWithSubqueryEntry; import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; -import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectForUpdate; @@ -96,7 +96,7 @@ public class OracleSelectParser extends SQLSelectParser { if (lexer.token() == Token.OF) { lexer.nextToken(); - this.exprParser.exprList(forUpdate.getOf()); + this.exprParser.exprList(forUpdate.getOf(), forUpdate); } if (lexer.token() == Token.NOWAIT) { @@ -207,7 +207,7 @@ public class OracleSelectParser extends SQLSelectParser { if (identifierEquals("CYCLE")) { lexer.nextToken(); CycleClause cycleClause = new CycleClause(); - exprParser.exprList(cycleClause.getAliases()); + exprParser.exprList(cycleClause.getAliases(), cycleClause); accept(Token.SET); cycleClause.setMark(exprParser.expr()); accept(Token.TO); @@ -510,7 +510,7 @@ public class OracleSelectParser extends SQLSelectParser { cellAssignment.setMeasureColumn(expr()); accept(Token.LBRACKET); - this.exprParser.exprList(cellAssignment.getConditions()); + this.exprParser.exprList(cellAssignment.getConditions(), cellAssignment); accept(Token.RBRACKET); return cellAssignment; @@ -524,10 +524,10 @@ public class OracleSelectParser extends SQLSelectParser { accept(Token.BY); if (lexer.token() == Token.LPAREN) { lexer.nextToken(); - exprParser.exprList(queryPartitionClause.getExprList()); + exprParser.exprList(queryPartitionClause.getExprList(), queryPartitionClause); accept(Token.RPAREN); } else { - exprParser.exprList(queryPartitionClause.getExprList()); + exprParser.exprList(queryPartitionClause.getExprList(), queryPartitionClause); } modelColumnClause.setQueryPartitionClause(queryPartitionClause); } @@ -573,7 +573,7 @@ public class OracleSelectParser extends SQLSelectParser { lexer.nextToken(); acceptIdentifier("SETS"); accept(Token.LPAREN); - exprParser.exprList(groupingSet.getParameters()); + exprParser.exprList(groupingSet.getParameters(), groupingSet); accept(Token.RPAREN); groupBy.addItem(groupingSet); } else { @@ -610,7 +610,7 @@ public class OracleSelectParser extends SQLSelectParser { lexer.nextToken(); acceptIdentifier("SETS"); accept(Token.LPAREN); - exprParser.exprList(groupingSet.getParameters()); + exprParser.exprList(groupingSet.getParameters(), groupingSet); accept(Token.RPAREN); groupBy.addItem(groupingSet); } else { @@ -760,7 +760,7 @@ public class OracleSelectParser extends SQLSelectParser { } accept(Token.LPAREN); - this.exprParser.exprList(sample.getPercent()); + this.exprParser.exprList(sample.getPercent(), sample); accept(Token.RPAREN); if (identifierEquals("SEED")) { @@ -959,7 +959,7 @@ public class OracleSelectParser extends SQLSelectParser { } else if (lexer.token() == Token.USING) { lexer.nextToken(); accept(Token.LPAREN); - this.exprParser.exprList(join.getUsing()); + this.exprParser.exprList(join.getUsing(), join); accept(Token.RPAREN); } @@ -984,7 +984,7 @@ public class OracleSelectParser extends SQLSelectParser { accept(Token.LPAREN); while (true) { item = new OracleSelectPivot.Item(); - item.setExpr((OracleAggregateExpr) this.exprParser.expr()); + item.setExpr((SQLAggregateExpr) this.exprParser.expr()); item.setAlias(as()); pivot.getItems().add(item); @@ -1060,7 +1060,7 @@ public class OracleSelectParser extends SQLSelectParser { if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); - this.exprParser.exprList(unPivot.getItems()); + this.exprParser.exprList(unPivot.getItems(), unPivot); accept(Token.RPAREN); } else { unPivot.getItems().add(this.exprParser.expr()); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java index b245b5d8f7..4ea1acdfba 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java @@ -965,7 +965,7 @@ public class OracleStatementParser extends SQLStatementParser { if (identifierEquals("AT")) { lexer.nextToken(); accept(Token.LPAREN); - this.exprParser.exprList(item.getAt()); + this.exprParser.exprList(item.getAt(), item); accept(Token.RPAREN); } else { throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); @@ -1206,12 +1206,12 @@ public class OracleStatementParser extends SQLStatementParser { if (lexer.token() == Token.LPAREN) { accept(Token.LPAREN); - exprParser.exprList(insertClause.getColumns()); + exprParser.exprList(insertClause.getColumns(), insertClause); accept(Token.RPAREN); } accept(Token.VALUES); accept(Token.LPAREN); - exprParser.exprList(insertClause.getValues()); + exprParser.exprList(insertClause.getValues(), insertClause); accept(Token.RPAREN); if (lexer.token() == Token.WHERE) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java index 1c9d44abee..c547e0ddd9 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java @@ -36,7 +36,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleWithSubqueryEntry; import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; -import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalytic; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalyticWindowing; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleArgumentExpr; @@ -123,8 +122,6 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public interface OracleASTVisitor extends SQLASTVisitor { - void endVisit(OracleAggregateExpr astNode); - void endVisit(OraclePLSQLCommitStatement astNode); void endVisit(OracleAnalytic x); @@ -169,8 +166,6 @@ public interface OracleASTVisitor extends SQLASTVisitor { void endVisit(OracleUpdateStatement x); - boolean visit(OracleAggregateExpr astNode); - boolean visit(OraclePLSQLCommitStatement astNode); boolean visit(OracleAnalytic x); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java index 8c8c3e276e..eae3fdcc9c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java @@ -44,7 +44,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleWithSubqueryEntry; import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; -import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalytic; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalyticWindowing; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleArgumentExpr; @@ -142,11 +141,6 @@ public class OracleASTVisitorAdapter extends SQLASTVisitorAdapter implements Ora public void endVisit(OracleSelect x) { } - @Override - public void endVisit(OracleAggregateExpr astNode) { - - } - @Override public void endVisit(OraclePLSQLCommitStatement astNode) { @@ -257,12 +251,6 @@ public class OracleASTVisitorAdapter extends SQLASTVisitorAdapter implements Ora } - @Override - public boolean visit(OracleAggregateExpr astNode) { - - return true; - } - @Override public boolean visit(OraclePLSQLCommitStatement astNode) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index 1cc7c7c279..f6ab1876ee 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -72,7 +72,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleWithSubqueryEntry; import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; -import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalytic; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalyticWindowing; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleArgumentExpr; @@ -218,32 +217,14 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } } - public boolean visit(OracleAggregateExpr expr) { - print(expr.getMethodName()); - print("("); - - if (expr.getOption() != null) { - print(expr.getOption().toString()); - print(' '); - } - - printAndAccept(expr.getArguments(), ", "); - print(")"); - - if (expr.getOver() != null) { - print(" OVER ("); - expr.getOver().accept(this); - print(")"); - } - return false; - } - public boolean visit(SQLAllColumnExpr x) { print("*"); return false; } public boolean visit(OracleAnalytic x) { + print("OVER ("); + boolean space = false; if (x.getPartitionBy().size() > 0) { print("PARTITION BY "); @@ -267,6 +248,8 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getWindowing().accept(this); } + print(")"); + return false; } @@ -810,13 +793,6 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS return false; } - // /////////////////// - - @Override - public void endVisit(OracleAggregateExpr astNode) { - - } - @Override public void endVisit(OraclePLSQLCommitStatement astNode) { @@ -2205,6 +2181,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS item.setParent(x); item.accept(this); } + print(";"); decrementIndent(); return false; @@ -2226,6 +2203,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS item.setParent(x); item.accept(this); } + print(";"); decrementIndent(); return false; @@ -2256,6 +2234,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS println(); } } + print(";"); decrementIndent(); for (ElseIf elseIf : x.getElseIfList()) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java index b76d7b9a67..7a6f2049c0 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java @@ -23,7 +23,6 @@ import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLOrderBy; -import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; @@ -68,7 +67,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleWithSubqueryEntry; import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; -import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalytic; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalyticWindowing; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleArgumentExpr; @@ -262,17 +260,6 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle return false; } - public boolean visit(SQLAggregateExpr x) { - accept(x.getArguments()); - return false; - } - - public boolean visit(OracleAggregateExpr x) { - accept(x.getArguments()); - accept(x.getOver()); - return false; - } - public void endVisit(OracleSelect x) { endVisit((SQLSelect) x); } @@ -400,11 +387,6 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle return super.visit(x); } - @Override - public void endVisit(OracleAggregateExpr astNode) { - - } - @Override public void endVisit(OraclePLSQLCommitStatement astNode) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGLexer.java index dc8fd11fa6..9939d3e10f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGLexer.java @@ -58,6 +58,9 @@ public class PGLexer extends Lexer { map.put("USING", Token.USING); map.put("WINDOW", Token.WINDOW); + + map.put("TRUE", Token.TRUE); + map.put("FALSE", Token.FALSE); DEFAULT_PG_KEYWORDS = new Keywords(map); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGSQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGSQLStatementParser.java index 124df2bdf8..523874cdc3 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGSQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGSQLStatementParser.java @@ -100,7 +100,7 @@ public class PGSQLStatementParser extends SQLStatementParser { if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); - this.exprParser.exprList(stmt.getColumns()); + this.exprParser.exprList(stmt.getColumns(), stmt); accept(Token.RPAREN); } @@ -110,7 +110,7 @@ public class PGSQLStatementParser extends SQLStatementParser { for (;;) { accept(Token.LPAREN); SQLInsertStatement.ValuesClause valuesCaluse = new SQLInsertStatement.ValuesClause(); - this.exprParser.exprList(valuesCaluse.getValues()); + this.exprParser.exprList(valuesCaluse.getValues(), valuesCaluse); stmt.addValueCause(valuesCaluse); accept(Token.RPAREN); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java index aab7796ba8..84abe762fd 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java @@ -100,6 +100,7 @@ public class SQLServerLexer extends Lexer { } else { stringVal = subString(mark, bufPos); token = Token.MULTI_LINE_COMMENT; + hasComment = true; } if (token != Token.HINT && !isAllowComment()) { @@ -142,6 +143,7 @@ public class SQLServerLexer extends Lexer { stringVal = subString(mark + 1, bufPos); token = Token.LINE_COMMENT; + hasComment = true; return; } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java index cfef12a6c1..f85241aaca 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java @@ -109,7 +109,7 @@ public class SQLServerStatementParser extends SQLStatementParser { if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); - this.exprParser.exprList(insertStatement.getColumns()); + this.exprParser.exprList(insertStatement.getColumns(), insertStatement); accept(Token.RPAREN); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerParameterizedOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerParameterizedOutputVisitor.java index 2653ce3384..74a75774bc 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerParameterizedOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerParameterizedOutputVisitor.java @@ -22,6 +22,7 @@ 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.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; import com.alibaba.druid.sql.visitor.ParameterizedVisitor; @@ -90,5 +91,13 @@ public class SQLServerParameterizedOutputVisitor extends SQLServerOutputVisitor return ParameterizedOutputVisitorUtils.visit(this, x); } + + public boolean visit(SQLVariantRefExpr x) { + if (!ParameterizedOutputVisitorUtils.checkParameterize(x) || x.getName().equalsIgnoreCase("?")) { + return super.visit(x); + } + + return ParameterizedOutputVisitorUtils.visit(this, x); + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java index f9032f994b..a8174a78eb 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java @@ -69,6 +69,8 @@ public class Lexer { private int varIndex = -1; protected CommentHandler commentHandler; + + protected boolean hasComment = false; public Lexer(String input){ this(input, true); @@ -747,6 +749,7 @@ public class Lexer { stringVal = subString(mark, bufPos); token = Token.MULTI_LINE_COMMENT; + hasComment = true; return; } @@ -777,6 +780,7 @@ public class Lexer { stringVal = subString(mark + 1, bufPos); token = Token.LINE_COMMENT; + hasComment = true; return; } } @@ -1045,4 +1049,8 @@ public class Lexer { boolean handle(Token lastToken, String comment); } + + public boolean isHasComment() { + return hasComment; + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java index c60beafe11..8170fca8f6 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -36,6 +36,7 @@ import com.alibaba.druid.sql.ast.expr.SQLAnyExpr; import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.expr.SQLCastExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; @@ -554,6 +555,14 @@ public class SQLExprParser extends SQLParser { break; case EOF: throw new EOFParserException(); + case TRUE: + lexer.nextToken(); + sqlExpr = new SQLBooleanExpr(true); + break; + case FALSE: + lexer.nextToken(); + sqlExpr = new SQLBooleanExpr(false); + break; default: throw new ParserException("ERROR. token : " + tok + ", pos : " + lexer.pos()); } @@ -714,6 +723,7 @@ public class SQLExprParser extends SQLParser { } } + @Deprecated public final void exprList(Collection exprCol) { exprList(exprCol, null); } @@ -878,10 +888,10 @@ public class SQLExprParser extends SQLParser { if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); - exprList(over.getPartitionBy()); + exprList(over.getPartitionBy(), over); accept(Token.RPAREN); } else { - exprList(over.getPartitionBy()); + exprList(over.getPartitionBy(), over); } } @@ -1359,29 +1369,19 @@ public class SQLExprParser extends SQLParser { } charType.setCharSetName(lexer.stringVal()); lexer.nextToken(); - - if (lexer.token() == Token.IDENTIFIER) { - if (lexer.stringVal().equalsIgnoreCase("COLLATE")) { - lexer.nextToken(); - - if (lexer.token() != Token.IDENTIFIER) { - throw new ParserException(); - } - charType.setCollate(lexer.stringVal()); - lexer.nextToken(); - } - } - }else if(identifierEquals("COLLATE")){ - lexer.nextToken(); - - if (lexer.token() != Token.IDENTIFIER) { - throw new ParserException(); - } - - charType.setCollate(lexer.stringVal()); - lexer.nextToken(); } + if (lexer.token() == Token.IDENTIFIER) { + if (lexer.stringVal().equalsIgnoreCase("COLLATE")) { + lexer.nextToken(); + + if (lexer.token() != Token.IDENTIFIER) { + throw new ParserException(); + } + charType.setCollate(lexer.stringVal()); + lexer.nextToken(); + } + } return charType; } @@ -1539,7 +1539,7 @@ public class SQLExprParser extends SQLParser { SQLPrimaryKeyImpl pk = new SQLPrimaryKeyImpl(); accept(Token.LPAREN); - exprList(pk.getColumns()); + exprList(pk.getColumns(), pk); accept(Token.RPAREN); return pk; @@ -1550,7 +1550,7 @@ public class SQLExprParser extends SQLParser { SQLUnique unique = new SQLUnique(); accept(Token.LPAREN); - exprList(unique.getColumns()); + exprList(unique.getColumns(), unique); accept(Token.RPAREN); return unique; diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java index bc40bb8f34..5882e9c6c2 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java @@ -166,7 +166,10 @@ public class SQLParser { } else if (lexer.token() == Token.CASE) { alias = lexer.token.name(); lexer.nextToken(); - } + } else if (lexer.token() == Token.USER) { + alias = lexer.stringVal(); + lexer.nextToken(); + } switch (lexer.token()) { case KEY: diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java index 8a18609ecb..dc4e67b2f9 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -32,6 +32,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnionOperator; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +import com.alibaba.druid.sql.ast.statement.SQLUnionQueryTableSource; import com.alibaba.druid.sql.ast.statement.SQLWithSubqueryClause; public class SQLSelectParser extends SQLParser { @@ -316,8 +317,12 @@ public class SQLSelectParser extends SQLParser { if (lexer.token() == Token.SELECT || lexer.token() == Token.WITH) { SQLSelect select = select(); accept(Token.RPAREN); - queryRest(select.getQuery()); - tableSource = new SQLSubqueryTableSource(select); + SQLSelectQuery query = queryRest(select.getQuery()); + if (query instanceof SQLUnionQuery) { + tableSource = new SQLUnionQueryTableSource((SQLUnionQuery) query); + } else { + tableSource = new SQLSubqueryTableSource(select); + } } else if (lexer.token() == Token.LPAREN) { tableSource = parseTableSource(); accept(Token.RPAREN); @@ -418,7 +423,7 @@ public class SQLSelectParser extends SQLParser { lexer.nextToken(); if (lexer.token() == Token.LPAREN) { lexer.nextToken(); - this.exprParser.exprList(join.getUsing()); + this.exprParser.exprList(join.getUsing(), join); accept(Token.RPAREN); } else { join.getUsing().add(this.expr()); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java index b37fb6b89a..18e5007243 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -1062,7 +1062,7 @@ public class SQLStatementParser extends SQLParser { if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); - this.exprParser.exprList(insertStatement.getColumns()); + this.exprParser.exprList(insertStatement.getColumns(), insertStatement); accept(Token.RPAREN); } @@ -1070,7 +1070,7 @@ public class SQLStatementParser extends SQLParser { lexer.nextToken(); accept(Token.LPAREN); SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(); - this.exprParser.exprList(values.getValues()); + this.exprParser.exprList(values.getValues(), values); insertStatement.setValues(values); accept(Token.RPAREN); } else if (acceptSubQuery && (lexer.token() == Token.SELECT || lexer.token() == Token.LPAREN)) { @@ -1135,7 +1135,7 @@ public class SQLStatementParser extends SQLParser { if (lexer.token() == Token.LPAREN) { lexer.nextToken(); - exprParser.exprList(stmt.getParameters()); + exprParser.exprList(stmt.getParameters(), stmt); accept(Token.RPAREN); } @@ -1392,7 +1392,7 @@ public class SQLStatementParser extends SQLParser { if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); SQLListExpr list = new SQLListExpr(); - this.exprParser.exprList(list.getItems()); + this.exprParser.exprList(list.getItems(), list); accept(Token.RPAREN); item.setColumn(list); } else { @@ -1466,7 +1466,7 @@ public class SQLStatementParser extends SQLParser { if (lexer.token() == Token.LPAREN) { lexer.nextToken(); - this.exprParser.exprList(createView.getColumns()); + this.exprParser.exprList(createView.getColumns(), createView); accept(Token.RPAREN); } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/ExportParameterVisitorUtils.java b/src/main/java/com/alibaba/druid/sql/visitor/ExportParameterVisitorUtils.java index 0aa97ab7f1..002d186f8f 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/ExportParameterVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/ExportParameterVisitorUtils.java @@ -20,11 +20,11 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLNumericLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; public class ExportParameterVisitorUtils { @@ -48,8 +48,8 @@ public class ExportParameterVisitorUtils { return new SQLVariantRefExpr("?"); } - if (param instanceof MySqlBooleanExpr) { - Object value = ((MySqlBooleanExpr) param).getValue(); + if (param instanceof SQLBooleanExpr) { + Object value = ((SQLBooleanExpr) param).getValue(); parameters.add(value); return new SQLVariantRefExpr("?"); } 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 4a329c2a74..9f0e35a1c1 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/ParameterizedOutputVisitorUtils.java @@ -24,6 +24,7 @@ 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; @@ -55,13 +56,18 @@ public class ParameterizedOutputVisitorUtils { return sql; } - SQLStatement stmt = statementList.get(0); - StringBuilder out = new StringBuilder(); ParameterizedVisitor visitor = createParameterizedOutputVisitor(out, dbType); - stmt.accept(visitor); - if (visitor.getReplaceCount() == 0) { + for (int i = 0; i < statementList.size(); i++) { + if (i > 0) { + out.append(";\n"); + } + SQLStatement stmt = statementList.get(i); + stmt.accept(visitor); + } + + if (visitor.getReplaceCount() == 0 && !parser.getLexer().isHasComment()) { return sql; } @@ -190,6 +196,18 @@ public class ParameterizedOutputVisitorUtils { return false; } + public static boolean visit(ParameterizedVisitor v, SQLVariantRefExpr x) { + v.print('?'); + v.incrementReplaceCunt(); + return false; + } + + public static boolean visit(ParameterizedVisitor v, SQLHexExpr x) { + v.print('?'); + v.incrementReplaceCunt(); + return false; + } + public static SQLBinaryOpExpr merge(ParameterizedVisitor v, SQLBinaryOpExpr x) { SQLExpr left = x.getLeft(); SQLExpr right = x.getRight(); @@ -198,8 +216,17 @@ public class ParameterizedOutputVisitorUtils { if (left instanceof SQLLiteralExpr && right instanceof SQLLiteralExpr) { if (x.getOperator() == SQLBinaryOperator.Equality // || x.getOperator() == SQLBinaryOperator.NotEqual) { - left.putAttribute(ATTR_PARAMS_SKIP, true); - right.putAttribute(ATTR_PARAMS_SKIP, true); + if((left instanceof SQLIntegerExpr) && (right instanceof SQLIntegerExpr) ) { + if (((SQLIntegerExpr) left).getNumber().intValue() < 100) { + left.putAttribute(ATTR_PARAMS_SKIP, true); + } + if (((SQLIntegerExpr) right).getNumber().intValue() < 100) { + right.putAttribute(ATTR_PARAMS_SKIP, true); + } + } else { + left.putAttribute(ATTR_PARAMS_SKIP, true); + right.putAttribute(ATTR_PARAMS_SKIP, true); + } } return x; } 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 cae14c8ca4..dc0e29b553 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -41,6 +41,7 @@ import com.alibaba.druid.sql.ast.expr.SQLAnyExpr; import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.expr.SQLCastExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; @@ -97,6 +98,7 @@ import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement; import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement.TriggerEvent; import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement.TriggerType; +import com.alibaba.druid.sql.ast.statement.SQLCreateViewStatement; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLDropDatabaseStatement; import com.alibaba.druid.sql.ast.statement.SQLDropFunctionStatement; @@ -134,6 +136,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; import com.alibaba.druid.sql.ast.statement.SQLTableElement; import com.alibaba.druid.sql.ast.statement.SQLTruncateStatement; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +import com.alibaba.druid.sql.ast.statement.SQLUnionQueryTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnique; import com.alibaba.druid.sql.ast.statement.SQLUniqueConstraint; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; @@ -536,6 +539,12 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab visitAggreateRest(x); print(")"); + + if (x.getWithinGroup() != null) { + print(" WITHIN GROUP ("); + x.getWithinGroup().accept(this); + print(")"); + } if (x.getOver() != null) { print(" "); @@ -601,7 +610,9 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab decrementIndent(); } else if (parent instanceof ValuesClause) { println(); + print("("); x.getSubQuery().accept(this); + print(")"); println(); } else { print("("); @@ -1411,6 +1422,27 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab x.getName().accept(this); return false; } + + @Override + public boolean visit(SQLCreateViewStatement x) { + print("CREATE "); + if(x.isOrReplace()) { + print("OR REPLACE "); + } + print("VIEW "); + x.getName().accept(this); + + if (x.getColumns().size() > 0) { + print(" ("); + printAndAccept(x.getColumns(), ", "); + print(")"); + } + + print(" AS "); + + x.getSubQuery().accept(this); + return false; + } @Override public boolean visit(SQLAlterTableDropIndex x) { @@ -1935,4 +1967,30 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab x.getBody().accept(this); return false; } + + public boolean visit(SQLBooleanExpr x) { + print(x.getValue() ? "true" : "false"); + + return false; + } + + public void endVisit(SQLBooleanExpr x) { + } + + @Override + public boolean visit(SQLUnionQueryTableSource x) { + print("("); + incrementIndent(); + x.getUnion().accept(this); + println(); + decrementIndent(); + print(")"); + + if (x.getAlias() != null) { + print(' '); + print(x.getAlias()); + } + + return false; + } } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java index cccb4aae41..d3e81f5445 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java @@ -26,6 +26,7 @@ import com.alibaba.druid.sql.ast.expr.SQLAllExpr; import com.alibaba.druid.sql.ast.expr.SQLAnyExpr; import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.expr.SQLCastExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; @@ -111,6 +112,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSetStatement; import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; import com.alibaba.druid.sql.ast.statement.SQLTruncateStatement; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +import com.alibaba.druid.sql.ast.statement.SQLUnionQueryTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnique; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; @@ -534,4 +536,13 @@ public interface SQLASTVisitor { void endVisit(SQLDropProcedureStatement x); boolean visit(SQLDropProcedureStatement x); + + void endVisit(SQLBooleanExpr x); + + boolean visit(SQLBooleanExpr x); + + void endVisit(SQLUnionQueryTableSource x); + + boolean visit(SQLUnionQueryTableSource x); + } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java index cd9b2b6ae7..0cbe8de94f 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java @@ -27,6 +27,7 @@ import com.alibaba.druid.sql.ast.expr.SQLAllExpr; import com.alibaba.druid.sql.ast.expr.SQLAnyExpr; import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.expr.SQLCastExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; @@ -113,6 +114,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSetStatement; import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; import com.alibaba.druid.sql.ast.statement.SQLTruncateStatement; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +import com.alibaba.druid.sql.ast.statement.SQLUnionQueryTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnique; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; @@ -1041,4 +1043,25 @@ public class SQLASTVisitorAdapter implements SQLASTVisitor { public boolean visit(SQLDropProcedureStatement x) { return true; } + + @Override + public void endVisit(SQLBooleanExpr x) { + + } + + @Override + public boolean visit(SQLBooleanExpr x) { + return true; + } + + @Override + public void endVisit(SQLUnionQueryTableSource x) { + + } + + @Override + public boolean visit(SQLUnionQueryTableSource x) { + return true; + } + } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorImpl.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorImpl.java index 6c016e0c01..85cf887c83 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorImpl.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorImpl.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLHexExpr; @@ -143,4 +144,11 @@ public class SQLEvalVisitorImpl extends SQLASTVisitorAdapter implements SQLEvalV public void unregisterFunction(String funcName) { functions.remove(funcName); } + + @Override + public boolean visit(SQLBooleanExpr x) { + x.getAttributes().put(EVAL_VALUE, x.getValue()); + return false; + } + } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java index 10e5359333..2101fc8d16 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java @@ -15,23 +15,6 @@ */ package com.alibaba.druid.sql.visitor; -import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_ERROR; -import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_EXPR; -import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_VALUE; -import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_VALUE_NULL; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - import com.alibaba.druid.DruidRuntimeException; import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLExpr; @@ -95,6 +78,23 @@ import com.alibaba.druid.util.Utils; import com.alibaba.druid.wall.spi.WallVisitorUtils; import com.alibaba.druid.wall.spi.WallVisitorUtils.WallConditionContext; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_ERROR; +import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_EXPR; +import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_VALUE; +import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_VALUE_NULL; + public class SQLEvalVisitorUtils { private static Map functions = new HashMap(); @@ -209,7 +209,7 @@ public class SQLEvalVisitorUtils { functions.put("trim", Trim.instance); functions.put("ucase", Ucase.instance); functions.put("upper", Ucase.instance); - functions.put("ucase", Lcase.instance); + functions.put("lcase", Lcase.instance); functions.put("lower", Lcase.instance); functions.put("hex", Hex.instance); functions.put("unhex", Unhex.instance); @@ -808,6 +808,11 @@ public class SQLEvalVisitorUtils { x.putAttribute(EVAL_VALUE, EVAL_ERROR); return false; } + + if (val == null) { + x.putAttribute(EVAL_VALUE, EVAL_VALUE_NULL); + return false; + } switch (x.getOperator()) { case BINARY: @@ -1112,11 +1117,15 @@ public class SQLEvalVisitorUtils { if (val == null) { return null; } + + if (val == EVAL_VALUE_NULL) { + return null; + } if (val instanceof Boolean) { return (Boolean) val; } - + if (val instanceof Number) { return ((Number) val).intValue() > 0; } @@ -1230,6 +1239,14 @@ public class SQLEvalVisitorUtils { return castToLong(list.get(0)); } } + + if (val instanceof Boolean) { + if (((Boolean) val).booleanValue()) { + return 1l; + } else { + return 0l; + } + } return ((Number) val).longValue(); } @@ -1273,6 +1290,53 @@ public class SQLEvalVisitorUtils { return BigInteger.valueOf(((Number) val).longValue()); } + + public static Number castToNumber(String val) { + if (val == null) { + return null; + } + + try { + return Byte.parseByte(val); + } catch (NumberFormatException e) { + } + + try { + return Short.parseShort(val); + } catch (NumberFormatException e) { + } + + try { + return Integer.parseInt(val); + } catch (NumberFormatException e) { + } + + try { + return Long.parseLong(val); + } catch (NumberFormatException e) { + } + + try { + return Float.parseFloat(val); + } catch (NumberFormatException e) { + } + + try { + return Double.parseDouble(val); + } catch (NumberFormatException e) { + } + + try { + return new BigInteger(val); + } catch (NumberFormatException e) { + } + + try { + return new BigDecimal(val); + } catch (NumberFormatException e) { + return 0; + } + } public static Date castToDate(Object val) { if (val == null) { @@ -1354,6 +1418,18 @@ public class SQLEvalVisitorUtils { if (a == null || b == null) { return null; } + + if(a == EVAL_VALUE_NULL || b == EVAL_VALUE_NULL) { + return null; + } + + if (a instanceof String) { + a = castToNumber((String) a); + } + + if (b instanceof String) { + b = castToNumber((String) b); + } if (a instanceof Long || b instanceof Long) { return castToLong(a).longValue() & castToLong(b).longValue(); @@ -1366,6 +1442,18 @@ public class SQLEvalVisitorUtils { if (a == null || b == null) { return null; } + + if(a == EVAL_VALUE_NULL || b == EVAL_VALUE_NULL) { + return null; + } + + if (a instanceof String) { + a = castToNumber((String) a); + } + + if (b instanceof String) { + b = castToNumber((String) b); + } if (a instanceof Long || b instanceof Long) { return castToLong(a).longValue() | castToLong(b).longValue(); @@ -1378,6 +1466,18 @@ public class SQLEvalVisitorUtils { if (a == null || b == null) { return null; } + + if(a == EVAL_VALUE_NULL || b == EVAL_VALUE_NULL) { + return null; + } + + if (a instanceof String) { + a = castToNumber((String) a); + } + + if (b instanceof String) { + b = castToNumber((String) b); + } if (a instanceof BigDecimal || b instanceof BigDecimal) { BigDecimal decimalA = castToDecimal(a); @@ -1415,7 +1515,18 @@ public class SQLEvalVisitorUtils { } if (a instanceof Long || b instanceof Long) { - return castToLong(a) / castToLong(b); + Long longA = castToLong(a); + Long longB = castToLong(b); + if (longB == 0) { + if (longA > 0) { + return Double.POSITIVE_INFINITY; + } else if (longA < 0) { + return Double.NEGATIVE_INFINITY; + } else { + return Double.NaN; + } + } + return longA / longB; } if (a instanceof Integer || b instanceof Integer) { @@ -1588,6 +1699,10 @@ public class SQLEvalVisitorUtils { if (a == null || b == null) { return false; } + + if (a == EVAL_VALUE_NULL || b == EVAL_VALUE_NULL) { + return false; + } if (a.equals(b)) { return true; @@ -1610,7 +1725,12 @@ public class SQLEvalVisitorUtils { } if (a instanceof Integer || b instanceof Integer) { - return castToInteger(a).equals(castToInteger(b)); + Integer inta = castToInteger(a); + Integer intb = castToInteger(b); + if (inta == null || intb == null) { + return false; + } + return inta.equals(intb); } if (a instanceof Short || b instanceof Short) { @@ -1655,6 +1775,14 @@ public class SQLEvalVisitorUtils { if (a == EVAL_VALUE_NULL || b == EVAL_VALUE_NULL) { return EVAL_VALUE_NULL; } + + if (a instanceof String && !(b instanceof String)) { + a = castToNumber((String) a); + } + + if (b instanceof String && !(a instanceof String)) { + b = castToNumber((String) b); + } if (a instanceof BigDecimal || b instanceof BigDecimal) { return castToDecimal(a).add(castToDecimal(b)); @@ -1695,7 +1823,7 @@ public class SQLEvalVisitorUtils { return castToByte(a) + castToByte(b); } - if (a instanceof String || b instanceof String) { + if (a instanceof String && b instanceof String) { return castToString(a) + castToString(b); } @@ -1718,6 +1846,14 @@ public class SQLEvalVisitorUtils { if (a instanceof Date || b instanceof Date) { return SQLEvalVisitor.EVAL_ERROR; } + + if (a instanceof String) { + a = castToNumber((String) a); + } + + if (b instanceof String) { + b = castToNumber((String) b); + } if (a instanceof BigDecimal || b instanceof BigDecimal) { return castToDecimal(a).subtract(castToDecimal(b)); @@ -1758,10 +1894,6 @@ public class SQLEvalVisitorUtils { return castToByte(a) - castToByte(b); } - if (a instanceof String && b instanceof String) { - return castToLong(a) - castToLong(b); - } - // return SQLEvalVisitor.EVAL_ERROR; throw new IllegalArgumentException(a.getClass() + " and " + b.getClass() + " not supported."); } @@ -1771,6 +1903,14 @@ public class SQLEvalVisitorUtils { return null; } + if (a instanceof String) { + a = castToNumber((String) a); + } + + if (b instanceof String) { + b = castToNumber((String) b); + } + if (a instanceof BigDecimal || b instanceof BigDecimal) { return castToDecimal(a).multiply(castToDecimal(b)); } @@ -1778,6 +1918,14 @@ public class SQLEvalVisitorUtils { if (a instanceof BigInteger || b instanceof BigInteger) { return castToBigInteger(a).multiply(castToBigInteger(b)); } + + if (a instanceof Double || b instanceof Double) { + return castToDouble(a) * castToDouble(b); + } + + if (a instanceof Float || b instanceof Float) { + return castToFloat(a) * castToFloat(b); + } if (a instanceof Long || b instanceof Long) { return castToLong(a) * castToLong(b); diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java index 3fd944310b..e7269db0ab 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java @@ -980,6 +980,8 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { public boolean visit(SQLAggregateExpr x) { accept(x.getArguments()); + accept(x.getWithinGroup()); + accept(x.getOver()); return false; } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/functions/Char.java b/src/main/java/com/alibaba/druid/sql/visitor/functions/Char.java index 27f9146982..301fb4352d 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/functions/Char.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/functions/Char.java @@ -42,8 +42,11 @@ public class Char implements Function { int charCode = ((Number) paramValue).intValue(); buf.append((char) charCode); } else if (paramValue instanceof String) { - int charCode = new BigDecimal((String)paramValue).intValue(); - buf.append((char) charCode); + try { + int charCode = new BigDecimal((String) paramValue).intValue(); + buf.append((char) charCode); + } catch (NumberFormatException e) { + } } else { return SQLEvalVisitor.EVAL_ERROR; } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/functions/Substring.java b/src/main/java/com/alibaba/druid/sql/visitor/functions/Substring.java index 9e11949825..fe6ecb9914 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/functions/Substring.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/functions/Substring.java @@ -50,7 +50,7 @@ public class Substring implements Function { if (paramSize == 2) { - if (index < 0) { + if (index <= 0) { int lastIndex = str.length() + index; return str.substring(lastIndex); } @@ -68,7 +68,7 @@ public class Substring implements Function { int len = ((Number) param2Value).intValue(); String result; - if (index < 0) { + if (index <= 0) { int lastIndex = str.length() + index; result = str.substring(lastIndex); } else { diff --git a/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java index b0956e85b5..775d8eb83e 100644 --- a/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java @@ -31,6 +31,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.druid.util.LRUCache; +import java.util.Collections; public class WebAppStat { @@ -504,7 +505,7 @@ public class WebAppStat { public List> getSessionStatDataList() { List> sessionStatDataList = new ArrayList>(this.sessionStatMap.size()); - for (WebSessionStat sessionStat : this.sessionStatMap.values()) { + for (WebSessionStat sessionStat : Collections.unmodifiableCollection(this.sessionStatMap.values())) { Map sessionStatData = sessionStat.getStatData(); int runningCount = ((Number) sessionStatData.get("RunningCount")).intValue(); @@ -642,7 +643,6 @@ public class WebAppStat { } } else if (isLinux) { osLinuxCount.incrementAndGet(); - isAndroid = computeUserAgentAndroid(userAgent); } else if (userAgent.indexOf("Symbian") != -1) { osSymbianCount.incrementAndGet(); diff --git a/src/main/java/com/alibaba/druid/util/JdbcConstants.java b/src/main/java/com/alibaba/druid/util/JdbcConstants.java index f203e65ce2..0feecaad52 100644 --- a/src/main/java/com/alibaba/druid/util/JdbcConstants.java +++ b/src/main/java/com/alibaba/druid/util/JdbcConstants.java @@ -59,4 +59,10 @@ public interface JdbcConstants { * 阿里云odps */ public static final String ODPS = "odps"; + + /** + * Log4JDBC + */ + public static final String LOG4JDBC = "log4jdbc"; + public static final String LOG4JDBC_DRIVER = "net.sf.log4jdbc.DriverSpy"; } diff --git a/src/main/java/com/alibaba/druid/util/JdbcUtils.java b/src/main/java/com/alibaba/druid/util/JdbcUtils.java index 4521d3f5e0..71afa830f3 100644 --- a/src/main/java/com/alibaba/druid/util/JdbcUtils.java +++ b/src/main/java/com/alibaba/druid/util/JdbcUtils.java @@ -351,6 +351,8 @@ public final class JdbcUtils implements JdbcConstants { return "org.apache.derby.jdbc.EmbeddedDriver"; } else if (rawUrl.startsWith("jdbc:mysql:")) { return MYSQL_DRIVER; + } else if (rawUrl.startsWith("jdbc:log4jdbc:")) { + return LOG4JDBC_DRIVER; } else if (rawUrl.startsWith("jdbc:mariadb:")) { return MARIADB_DRIVER; } else if (rawUrl.startsWith("jdbc:oracle:") // @@ -421,6 +423,8 @@ public final class JdbcUtils implements JdbcConstants { return DERBY; } else if (rawUrl.startsWith("jdbc:mysql:")) { return MYSQL; + } else if (rawUrl.startsWith("jdbc:log4jdbc:")) { + return LOG4JDBC; } else if (rawUrl.startsWith("jdbc:mariadb:")) { return MARIADB; } else if (rawUrl.startsWith("jdbc:oracle:")) { @@ -429,6 +433,8 @@ public final class JdbcUtils implements JdbcConstants { return ALI_ORACLE; } else if (rawUrl.startsWith("jdbc:microsoft:")) { return SQL_SERVER; + } else if (rawUrl.startsWith("jdbc:sqlserver:")) { + return SQL_SERVER; } else if (rawUrl.startsWith("jdbc:sybase:Tds:")) { return SYBASE; } else if (rawUrl.startsWith("jdbc:jtds:")) { diff --git a/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java b/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java index 3bd6090c3d..401c45a0ff 100644 --- a/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java +++ b/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java @@ -17,45 +17,56 @@ package com.alibaba.druid.util; public class ServletPathMatcher implements PatternMatcher { private final static ServletPathMatcher instance = new ServletPathMatcher(); - + public static ServletPathMatcher getInstance() { return instance; } - /** - *

- * three type: endsWithMatch(eg. /xxx*=/xxx/xyz), startsWithMatch(eg. *.xxx=abc.xxx), equals(eg. /xxx=/xxx). - *

- * Notice: *xxx* will match *xxxyyyy. endsWithMatch first. - */ - @Override - public boolean matches(String pattern, String source) { - if (pattern == null || source == null) { - return false; - } - pattern = pattern.trim(); - source = source.trim(); - if (pattern.endsWith("*")) { - // pattern: /druid* source:/druid/index.html - int length = pattern.length() - 1; - if (source.length() >= length) { - if (pattern.substring(0, length).equals(source.substring(0, length))) { - return true; - } - } - } else if (pattern.startsWith("*")) { - // pattern: *.html source:/xx/xx.html - int length = pattern.length() - 1; - if (source.length() >= length && source.endsWith(pattern.substring(1))) { - return true; - } - } else { - // pattern: /druid/index.html source:/druid/index.html - if (pattern.equals(source)) { - return true; - } - } - return false; - } + /** + *

+ * three type: endsWithMatch(eg. /xxx*=/xxx/xyz), startsWithMatch(eg. + * *.xxx=abc.xxx), equals(eg. /xxx=/xxx). + *

+ * Notice: *xxx* will match *xxxyyyy. endsWithMatch first. + */ + @Override + public boolean matches(String pattern, String source) { + if (pattern == null || source == null) { + return false; + } + pattern = pattern.trim(); + source = source.trim(); + if (pattern.endsWith("*")) { + // pattern: /druid* source:/druid/index.html + int length = pattern.length() - 1; + if (source.length() >= length) { + if (pattern.substring(0, length).equals( + source.substring(0, length))) { + return true; + } + } + } else if (pattern.startsWith("*")) { + // pattern: *.html source:/xx/xx.html + int length = pattern.length() - 1; + if (source.length() >= length + && source.endsWith(pattern.substring(1))) { + return true; + } + } else if (pattern.contains("*")) { + // pattern: /druid/*/index.html source:/druid/admin/index.html + int start = pattern.indexOf("*"); + int end = pattern.lastIndexOf("*"); + if (source.startsWith(pattern.substring(0, start)) + && source.endsWith(pattern.substring(end + 1))) { + return true; + } + } else { + // pattern: /druid/index.html source:/druid/index.html + if (pattern.equals(source)) { + return true; + } + } + return false; + } } diff --git a/src/main/java/com/alibaba/druid/wall/WallProvider.java b/src/main/java/com/alibaba/druid/wall/WallProvider.java index b5dc28d278..64eec2e5d4 100644 --- a/src/main/java/com/alibaba/druid/wall/WallProvider.java +++ b/src/main/java/com/alibaba/druid/wall/WallProvider.java @@ -641,7 +641,9 @@ public abstract class WallProvider { violations.add(new SyntaxErrorViolation(e, sql)); } } catch (Exception e) { - violations.add(new SyntaxErrorViolation(e, sql)); + if (config.isStrictSyntaxCheck()) { + violations.add(new SyntaxErrorViolation(e, sql)); + } } if (statementList.size() > 1 && !config.isMultiStatementAllow()) { @@ -664,15 +666,15 @@ public abstract class WallProvider { violations.addAll(visitor.getViolations()); } - if (visitor.getViolations().size() == 0 && context != null && context.getWarnnings() >= 2) { - if (context.getCommentCount() > 0) { - violations.add(new IllegalSQLObjectViolation(ErrorCode.COMMIT_NOT_ALLOW, "comment not allow", sql)); - } else if (context.getLikeNumberWarnnings() > 0) { - violations.add(new IllegalSQLObjectViolation(ErrorCode.COMMIT_NOT_ALLOW, "like number", sql)); - } else { - violations.add(new IllegalSQLObjectViolation(ErrorCode.COMPOUND, "multi-warnnings", sql)); - } - } + // if (visitor.getViolations().size() == 0 && context != null && context.getWarnnings() >= 2) { + // if (context.getCommentCount() > 0) { + // violations.add(new IllegalSQLObjectViolation(ErrorCode.COMMIT_NOT_ALLOW, "comment not allow", sql)); + // } else if (context.getLikeNumberWarnnings() > 0) { + // violations.add(new IllegalSQLObjectViolation(ErrorCode.COMMIT_NOT_ALLOW, "like number", sql)); + // } else { + // violations.add(new IllegalSQLObjectViolation(ErrorCode.COMPOUND, "multi-warnnings", sql)); + // } + // } WallSqlStat sqlStat = null; if (violations.size() > 0) { diff --git a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java index 5fd1d05463..942cacc63e 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java @@ -40,6 +40,7 @@ import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.expr.SQLCaseExpr.Item; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; @@ -91,7 +92,6 @@ import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; import com.alibaba.druid.sql.ast.statement.SQLUseStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; @@ -1386,8 +1386,8 @@ public class WallVisitorUtils { return getValue(visitor, (SQLBinaryOpExpr) x); } - if (x instanceof MySqlBooleanExpr) { - return ((MySqlBooleanExpr) x).getValue(); + if (x instanceof SQLBooleanExpr) { + return ((SQLBooleanExpr) x).getValue(); } if (x instanceof SQLNumericLiteralExpr) { diff --git a/src/main/resources/META-INF/druid-driver.properties b/src/main/resources/META-INF/druid-driver.properties index 95f94a1c44..9b1fcc926c 100644 --- a/src/main/resources/META-INF/druid-driver.properties +++ b/src/main/resources/META-INF/druid-driver.properties @@ -1,6 +1,7 @@ jdbc:derby:=org.apache.derby.jdbc.EmbeddedDriver -jdbc:mysql:=com.mysql.jdbc.Driver -jdbc:oracle:=oracle.jdbc.driver.OracleDriver +jdbc:mysql:=com.mysql.jdbc.Driver +jdbc:log4jdbc:=net.sf.log4jdbc.DriverSpy +jdbc:oracle:=oracle.jdbc.driver.OracleDriver jdbc:microsoft:=com.microsoft.jdbc.sqlserver.SQLServerDriver jdbc:jtds:=net.sourceforge.jtds.jdbc.Driver jdbc:postgresql:=org.postgresql.Driver diff --git a/src/main/resources/support/http/resources/js/doT.js b/src/main/resources/support/http/resources/js/doT.js index 5019fad26f..7422b308ae 100644 --- a/src/main/resources/support/http/resources/js/doT.js +++ b/src/main/resources/support/http/resources/js/doT.js @@ -6,7 +6,7 @@ "use strict"; var doT = { - version: '1.0.3', + version: '1.0.7', templateSettings: { evaluate: /\{\{([\s\S]+?\}?)\}\}/g, interpolate: /\{\{=([\s\S]+?)\}\}/g, diff --git a/src/test/java/com/alibaba/druid/DruidTest.java b/src/test/java/com/alibaba/druid/DruidTest.java deleted file mode 100644 index 13ae9d095c..0000000000 --- a/src/test/java/com/alibaba/druid/DruidTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 1999-2011 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.druid; - -import junit.framework.TestCase; - -import com.alibaba.druid.pool.DruidDataSource; - -public class DruidTest extends TestCase { - - public void test_0() throws Exception { - DruidDataSource ds = new DruidDataSource(); - ds.setMaxWait(30); - ds.setInitialSize(0); - ds.setUrl("jdbc:mysql:xxx"); - - try { - ds.getConnection(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } -} diff --git a/src/test/java/com/alibaba/druid/TestRollBack.java b/src/test/java/com/alibaba/druid/TestRollBack.java index 9a07206a86..08cae1c912 100644 --- a/src/test/java/com/alibaba/druid/TestRollBack.java +++ b/src/test/java/com/alibaba/druid/TestRollBack.java @@ -1,11 +1,9 @@ package com.alibaba.druid; -import java.beans.PropertyVetoException; -import java.sql.SQLException; - -import org.junit.Assert; - +import com.alibaba.druid.pool.DruidDataSource; +import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -17,8 +15,8 @@ import org.nutz.dao.impl.NutDao; import org.nutz.trans.Atom; import org.nutz.trans.Trans; -import com.alibaba.druid.pool.DruidDataSource; -import com.mchange.v2.c3p0.ComboPooledDataSource; +import java.beans.PropertyVetoException; +import java.sql.SQLException; /* * Copyright 1999-2011 Alibaba Group Holding Ltd. @@ -79,7 +77,7 @@ public class TestRollBack { } @AfterClass - public static void destory() { + public static void destroy() { c3p0.close(); druid.close(); } diff --git a/src/test/java/com/alibaba/druid/bvt/bug/Bug_for_JeffYin.java b/src/test/java/com/alibaba/druid/bvt/bug/Bug_for_JeffYin.java deleted file mode 100644 index 717f95c9eb..0000000000 --- a/src/test/java/com/alibaba/druid/bvt/bug/Bug_for_JeffYin.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 1999-2011 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.druid.bvt.bug; - -import java.sql.Driver; - -import junit.framework.TestCase; - -import org.junit.Assert; - -import com.alibaba.druid.util.JdbcUtils; -import com.alibaba.druid.util.Utils; - -public class Bug_for_JeffYin extends TestCase { - - public void test_0() throws Exception { - String url = "jdbc:sqlserver://localhost:1433;"; - String driverClassName = JdbcUtils.getDriverClassName(url); - Class driverClass = Utils.loadClass(driverClassName); - Assert.assertNotNull(driverClass); - - Driver driver = (Driver) driverClass.newInstance(); - - Assert.assertTrue(driver.acceptsURL(url)); - } -} diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest80.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest80.java index ad2e23d3f6..c3fd1e2213 100644 --- a/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest80.java +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest80.java @@ -45,7 +45,7 @@ public class MySqlWallTest80 extends TestCase { provider.getConfig().setCommentAllow(true); - Assert.assertFalse(provider.checkValid(// + Assert.assertTrue(provider.checkValid(// "SELECT * FROM T WHERE FID = ? OR FID LIKE 1 --")); Assert.assertEquals(1, provider.getTableStats().size()); diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/TenantSelectTest3.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/TenantSelectTest3.java index a01167bf0e..8f5bb27e26 100644 --- a/src/test/java/com/alibaba/druid/bvt/filter/wall/TenantSelectTest3.java +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/TenantSelectTest3.java @@ -28,26 +28,27 @@ import com.alibaba.druid.wall.spi.MySqlWallProvider; public class TenantSelectTest3 extends TestCase { - private String sql = "SELECT ID, NAME " + // - "FROM orders o inner join users u ON o.userid = u.id " + // - "WHERE FID = ? OR FID = ?"; - private String expect_sql = "SELECT ID, NAME, u.tenant, o.tenant" + // - "\nFROM orders o" + // - "\n\tINNER JOIN users u ON o.userid = u.id" + // - "\nWHERE FID = ?" + // - "\n\tOR FID = ?"; - - private WallConfig config = new WallConfig(); - private WallConfig config_callback = new WallConfig(); + private String sql = "SELECT ID, NAME " + // + "FROM orders o inner join users u ON o.userid = u.id " + // + "WHERE FID = ? OR FID = ?"; + private String expect_sql = "SELECT ID, NAME, u.tenant, o.tenant" + // + "\nFROM orders o" + // + "\n\tINNER JOIN users u ON o.userid = u.id" + // + "\nWHERE FID = ?" + // + "\n\tOR FID = ?"; protected void setUp() throws Exception { + + } + + public void testMySql() throws Exception { + WallConfig config = new WallConfig(); + WallConfig config_callback = new WallConfig(); config.setTenantTablePattern("*"); config.setTenantColumn("tenant"); config_callback.setTenantCallBack(new TenantTestCallBack()); - } - - public void testMySql() throws Exception { + WallProvider.setTenantValue(123); MySqlWallProvider provider = new MySqlWallProvider(config); WallCheckResult checkResult = provider.check(sql); @@ -58,6 +59,13 @@ public class TenantSelectTest3 extends TestCase { } public void testMySql2() throws Exception { + WallConfig config = new WallConfig(); + WallConfig config_callback = new WallConfig(); + config.setTenantTablePattern("*"); + config.setTenantColumn("tenant"); + + config_callback.setTenantCallBack(new TenantTestCallBack()); + MySqlWallProvider provider = new MySqlWallProvider(config_callback); WallCheckResult checkResult = provider.check(sql); Assert.assertEquals(0, checkResult.getViolations().size()); diff --git a/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceTest_fill.java b/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceTest_fill.java new file mode 100644 index 0000000000..724ae1a656 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceTest_fill.java @@ -0,0 +1,56 @@ +package com.alibaba.druid.bvt.pool; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.pool.DruidDataSource; + +public class DruidDataSourceTest_fill extends TestCase { + + private DruidDataSource dataSource; + + private int maxActive = 10; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setMaxActive(maxActive); + dataSource.setTestOnBorrow(false); + } + + protected void tearDown() throws Exception { + dataSource.close(); + } + + public void test_fill_0() throws Exception { + int fillCount = dataSource.fill(3); + Assert.assertEquals(3, fillCount); + } + + public void test_fill_1() throws Exception { + int fillCount = dataSource.fill(1000); + Assert.assertEquals(maxActive, fillCount); + } + + public void test_fill_2() throws Exception { + int fillCount = dataSource.fill(maxActive); + Assert.assertEquals(maxActive, fillCount); + } + + public void test_fill_3() throws Exception { + int fillCount = dataSource.fill(); + Assert.assertEquals(maxActive, fillCount); + } + + public void test_fill_5() throws Exception { + Exception error = null; + try { + dataSource.fill(-1); + } catch (IllegalArgumentException e) { + error = e; + } + Assert.assertNotNull(error); + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceTest_fill2.java b/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceTest_fill2.java new file mode 100644 index 0000000000..789723c6eb --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceTest_fill2.java @@ -0,0 +1,86 @@ +package com.alibaba.druid.bvt.pool; + +import java.sql.Connection; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.pool.DruidDataSource; + +public class DruidDataSourceTest_fill2 extends TestCase { + + private DruidDataSource dataSource; + + private int maxActive = 100; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setMaxActive(maxActive); + dataSource.setTestOnBorrow(false); + } + + protected void tearDown() throws Exception { + dataSource.close(); + } + + public void test_fill() throws Exception { + final AtomicLong errorCount = new AtomicLong(); + + final int THREAD_COUNT = 100; + final CountDownLatch startLatch = new CountDownLatch(THREAD_COUNT); + final CountDownLatch endLatch = new CountDownLatch(THREAD_COUNT); + Thread[] threads = new Thread[THREAD_COUNT]; + for (int i = 0; i < THREAD_COUNT; ++i) { + threads[i] = new Thread("thread-" + i) { + + public void run() { + startLatch.countDown(); + try { + Connection conn = dataSource.getConnection(); + Thread.sleep(2); + conn.close(); + } catch (Exception e) { + errorCount.incrementAndGet(); + } finally { + endLatch.countDown(); + } + } + }; + threads[i].start(); + } + + startLatch.await(1000, TimeUnit.MILLISECONDS); + + final CountDownLatch fillLatch = new CountDownLatch(THREAD_COUNT); + final AtomicLong fillErrorCount = new AtomicLong(); + final AtomicLong fillCount = new AtomicLong(); + Thread fillThread = new Thread() { + + public void run() { + try { + int count = dataSource.fill(); + fillCount.getAndSet(count); + } catch (Exception e) { + fillErrorCount.incrementAndGet(); + } finally { + fillLatch.countDown(); + } + } + }; + fillThread.start(); + + fillLatch.await(1000, TimeUnit.MILLISECONDS); + + Assert.assertEquals(0, fillErrorCount.get()); + + endLatch.await(1000, TimeUnit.MILLISECONDS); + + Assert.assertEquals(true, fillCount.get() > 0); + + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceTest_tryGet.java b/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceTest_tryGet.java new file mode 100644 index 0000000000..d337cec62b --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/DruidDataSourceTest_tryGet.java @@ -0,0 +1,37 @@ +package com.alibaba.druid.bvt.pool; + +import org.junit.Assert; + +import junit.framework.TestCase; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.pool.DruidPooledConnection; + +public class DruidDataSourceTest_tryGet extends TestCase { + + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setMaxActive(1); + dataSource.setInitialSize(1); + dataSource.setTestOnBorrow(false); + dataSource.init(); + } + + protected void tearDown() throws Exception { + dataSource.close(); + } + + public void test_0() throws Exception { + DruidPooledConnection conn1 = this.dataSource.tryGetConnection(); + Assert.assertNotNull(conn1); + + DruidPooledConnection conn2 = this.dataSource.tryGetConnection(); + Assert.assertNull(conn2); + + conn1.close(); + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/FullTest.java b/src/test/java/com/alibaba/druid/bvt/pool/FullTest.java new file mode 100644 index 0000000000..7a66d6027c --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/FullTest.java @@ -0,0 +1,37 @@ +package com.alibaba.druid.bvt.pool; + +import java.sql.Connection; + +import org.junit.Assert; + +import junit.framework.TestCase; + +import com.alibaba.druid.pool.DruidDataSource; + +public class FullTest extends TestCase { + + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setTestOnBorrow(false); + + dataSource.init(); + } + + protected void tearDown() throws Exception { + dataSource.close(); + } + + public void test_restart() throws Exception { + Assert.assertEquals(false, dataSource.isFull()); + dataSource.fill(); + + Assert.assertEquals(true, dataSource.isFull()); + Connection conn = dataSource.getConnection(); + Assert.assertEquals(true, dataSource.isFull()); + conn.close(); + Assert.assertEquals(true, dataSource.isFull()); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/property/PropertyTest_useGloalDataSourceStat.java b/src/test/java/com/alibaba/druid/bvt/pool/property/PropertyTest_useGlobalDataSourceStat.java similarity index 56% rename from src/test/java/com/alibaba/druid/bvt/pool/property/PropertyTest_useGloalDataSourceStat.java rename to src/test/java/com/alibaba/druid/bvt/pool/property/PropertyTest_useGlobalDataSourceStat.java index 0972a1379d..e6d97441e4 100644 --- a/src/test/java/com/alibaba/druid/bvt/pool/property/PropertyTest_useGloalDataSourceStat.java +++ b/src/test/java/com/alibaba/druid/bvt/pool/property/PropertyTest_useGlobalDataSourceStat.java @@ -8,23 +8,23 @@ import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.util.JdbcUtils; -public class PropertyTest_useGloalDataSourceStat extends TestCase { +public class PropertyTest_useGlobalDataSourceStat extends TestCase { private DruidDataSource dataSource; public void test_true() { - System.setProperty("druid.useGloalDataSourceStat", "true"); + System.setProperty("druid.useGlobalDataSourceStat", "true"); dataSource = new DruidDataSource(); - Assert.assertTrue(dataSource.isUseGloalDataSourceStat()); + Assert.assertTrue(dataSource.isUseGlobalDataSourceStat()); } public void test_false() { - System.setProperty("druid.useGloalDataSourceStat", "false"); + System.setProperty("druid.useGlobalDataSourceStat", "false"); dataSource = new DruidDataSource(); - Assert.assertFalse(dataSource.isUseGloalDataSourceStat()); + Assert.assertFalse(dataSource.isUseGlobalDataSourceStat()); } protected void tearDown() throws Exception { - System.clearProperty("druid.useGloalDataSourceStat"); + System.clearProperty("druid.useGlobalDataSourceStat"); JdbcUtils.close(dataSource); } } diff --git a/src/test/java/com/alibaba/druid/bvt/proxy/filter/GlobalStatTest0.java b/src/test/java/com/alibaba/druid/bvt/proxy/filter/GlobalStatTest0.java index d0a24ba237..5c93aaf04a 100644 --- a/src/test/java/com/alibaba/druid/bvt/proxy/filter/GlobalStatTest0.java +++ b/src/test/java/com/alibaba/druid/bvt/proxy/filter/GlobalStatTest0.java @@ -28,12 +28,12 @@ public class GlobalStatTest0 extends TestCase { dataSourceA = new DruidDataSource(); dataSourceA.setUrl("jdbc:mock:xx_A"); dataSourceA.setFilters("stat"); - dataSourceA.setUseGloalDataSourceStat(true); + dataSourceA.setUseGlobalDataSourceStat(true); dataSourceB = new DruidDataSource(); dataSourceB.setUrl("jdbc:mock:xx_A"); dataSourceB.setFilters("stat"); - dataSourceB.setUseGloalDataSourceStat(true); + dataSourceB.setUseGlobalDataSourceStat(true); } protected void tearDown() throws Exception { diff --git a/src/test/java/com/alibaba/druid/bvt/proxy/filter/GlobalStatTest1.java b/src/test/java/com/alibaba/druid/bvt/proxy/filter/GlobalStatTest1.java index 74199be9ca..5008cbbf3c 100644 --- a/src/test/java/com/alibaba/druid/bvt/proxy/filter/GlobalStatTest1.java +++ b/src/test/java/com/alibaba/druid/bvt/proxy/filter/GlobalStatTest1.java @@ -25,12 +25,12 @@ public class GlobalStatTest1 extends TestCase { dataSourceA = new DruidDataSource(); dataSourceA.setUrl("jdbc:mock:xx_A"); dataSourceA.setFilters("stat"); - dataSourceA.setUseGloalDataSourceStat(true); + dataSourceA.setUseGlobalDataSourceStat(true); dataSourceB = new DruidDataSource(); dataSourceB.setUrl("jdbc:mock:xx_A"); dataSourceB.setFilters("stat"); - dataSourceB.setUseGloalDataSourceStat(true); + dataSourceB.setUseGlobalDataSourceStat(true); } protected void tearDown() throws Exception { diff --git a/src/test/java/com/alibaba/druid/bvt/sql/EqualTest_boolean.java b/src/test/java/com/alibaba/druid/bvt/sql/EqualTest_boolean.java index 9bca1c33bc..ae68890016 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/EqualTest_boolean.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/EqualTest_boolean.java @@ -4,7 +4,7 @@ import junit.framework.TestCase; import org.junit.Assert; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlExprParser; import com.alibaba.druid.sql.parser.SQLExprParser; @@ -13,18 +13,18 @@ public class EqualTest_boolean extends TestCase { public void test_exits() throws Exception { String sql = "true"; String sql_c = "false"; - MySqlBooleanExpr exprA, exprB, exprC; + SQLBooleanExpr exprA, exprB, exprC; { SQLExprParser parser = new MySqlExprParser(sql); - exprA = (MySqlBooleanExpr) parser.expr(); + exprA = (SQLBooleanExpr) parser.expr(); } { SQLExprParser parser = new MySqlExprParser(sql); - exprB = (MySqlBooleanExpr) parser.expr(); + exprB = (SQLBooleanExpr) parser.expr(); } { SQLExprParser parser = new MySqlExprParser(sql_c); - exprC = (MySqlBooleanExpr) parser.expr(); + exprC = (SQLBooleanExpr) parser.expr(); } Assert.assertEquals(exprA, exprB); Assert.assertNotEquals(exprA, exprC); @@ -32,7 +32,7 @@ public class EqualTest_boolean extends TestCase { Assert.assertFalse(exprA.equals(new Object())); Assert.assertEquals(exprA.hashCode(), exprB.hashCode()); - Assert.assertEquals(new MySqlBooleanExpr(), new MySqlBooleanExpr()); - Assert.assertEquals(new MySqlBooleanExpr().hashCode(), new MySqlBooleanExpr().hashCode()); + Assert.assertEquals(new SQLBooleanExpr(), new SQLBooleanExpr()); + Assert.assertEquals(new SQLBooleanExpr().hashCode(), new SQLBooleanExpr().hashCode()); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/eval/SQLEvalVisitorUtilsTest.java b/src/test/java/com/alibaba/druid/bvt/sql/eval/SQLEvalVisitorUtilsTest.java index 9c5e1c59e1..66fe661d09 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/eval/SQLEvalVisitorUtilsTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/eval/SQLEvalVisitorUtilsTest.java @@ -29,6 +29,13 @@ public class SQLEvalVisitorUtilsTest extends TestCase { Assert.assertEquals(-1, SQLEvalVisitorUtils.evalExpr(JdbcUtils.MYSQL, "? - ?", Arrays. asList(2, 3))); Assert.assertEquals(2, SQLEvalVisitorUtils.evalExpr(JdbcUtils.MYSQL, "? / ?", Arrays. asList(6, 3))); } + + public void test_evalExpr_3() throws Exception { + Assert.assertEquals(5.0f, SQLEvalVisitorUtils.evalExpr(JdbcUtils.MYSQL, "? + ?", "2.0", 3)); + Assert.assertEquals(6.0f, SQLEvalVisitorUtils.evalExpr(JdbcUtils.MYSQL, "? * ?", "2.0", 3)); + Assert.assertEquals(-1.0f, SQLEvalVisitorUtils.evalExpr(JdbcUtils.MYSQL, "? - ?", "2.0", 3)); + Assert.assertEquals(2.0f, SQLEvalVisitorUtils.evalExpr(JdbcUtils.MYSQL, "? / ?", "6.0", 3)); + } public void test_add() throws Exception { Assert.assertEquals(5, SQLEvalVisitorUtils.evalExpr(JdbcUtils.MYSQL, "? + ?", Arrays. asList(2, 3))); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlASTVisitorAdapterTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlASTVisitorAdapterTest.java index 0ecb814c19..0ff4606c99 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlASTVisitorAdapterTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlASTVisitorAdapterTest.java @@ -2,6 +2,7 @@ package com.alibaba.druid.bvt.sql.mysql; import junit.framework.TestCase; +import com.alibaba.druid.sql.ast.expr.SQLBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlForceIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlIgnoreIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; @@ -9,7 +10,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBinaryExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlBooleanExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; @@ -101,7 +101,7 @@ public class MySqlASTVisitorAdapterTest extends TestCase { public void test_adapter() throws Exception { MySqlASTVisitorAdapter adapter = new MySqlASTVisitorAdapter(); - new MySqlBooleanExpr().accept(adapter); + new SQLBooleanExpr().accept(adapter); new Limit().accept(adapter); new MySqlTableIndex().accept(adapter); new MySqlKey().accept(adapter); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateTableTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateTableTest.java index b772a773f8..41f5785006 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateTableTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateTableTest.java @@ -53,4 +53,57 @@ public class MySqlCreateTableTest extends MysqlTest { // Assert.assertTrue(visitor.getColumns().contains(new Column("mytable", "last_name"))); } + + + public void test_1() throws Exception { + String sql = "CREATE TABLE `ins_ebay_auth` (" + + " `auth_id` int(10) NOT NULL AUTO_INCREMENT COMMENT '主键id'," + + " `usr_id` int(10) NOT NULL COMMENT '外键,用户表'," + + " `status` char(1) COLLATE utf8_bin NOT NULL COMMENT '状态 0.有效?1.无效'," + + " `ebay_token` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'eBay授权码'," + + " `ebay_name` varchar(50) COLLATE utf8_bin NOT NULL COMMENT 'eBay唯一名'," + + " `create_time` datetime NOT NULL COMMENT '授权时间', " + + " `invalid_time` datetime NOT NULL COMMENT '授权失效时间'," + + " PRIMARY KEY (`auth_id`)" + + " ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='INS_EBAY_AUTH'"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + statemen.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(7, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + } + + + public void test_2() throws Exception { + String sql = "create table t2 as select * from t1"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + statemen.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlInsertTest_9.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlInsertTest_9.java new file mode 100644 index 0000000000..0f5ca7241f --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlInsertTest_9.java @@ -0,0 +1,50 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.mysql; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; + +public class MySqlInsertTest_9 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "insert into sequence values('seq_wlb_order_log',268234128+10000000,now());"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + SQLInsertStatement insertStmt = (SQLInsertStatement) stmt; + + Assert.assertEquals(3, insertStmt.getValues().getValues().size()); + Assert.assertEquals(0, insertStmt.getColumns().size()); + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + stmt.accept(visitor); + + Assert.assertEquals("INSERT INTO sequence" + + "\nVALUES ('seq_wlb_order_log', 268234128 + 10000000, now())", SQLUtils.toMySqlString(insertStmt)); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_18.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_18.java new file mode 100644 index 0000000000..f3f1133328 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_18.java @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.mysql; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; + +public class MySqlSelectTest_18 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "SELECT host.id as id, host.item_id as itemId, host.node_id as nodeId, host.node_type as nodeType, host.begin_time as beginTime, host.end_time as endTime, host.gmt_create as gmtCreate, host.gmt_modify as gmtModify, host.reason as reason, host.creator_id as creatorId, host.modifier_id as modifierId, user.name as creator, user.name as modifier, user.nick_name as nickName FROM notice_close_node host left join sys_user user on user.id = host.modifier_id"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + statemen.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(2, visitor.getTables().size()); + Assert.assertEquals(14, visitor.getColumns().size()); + Assert.assertEquals(2, visitor.getConditions().size()); + + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/SQLSelectTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/SQLSelectTest.java index 4d2e04a367..b796a6f44e 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/SQLSelectTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/SQLSelectTest.java @@ -88,6 +88,15 @@ public class SQLSelectTest extends TestCase { output(stmtList); } + + public void test_select_7() throws Exception { + String sql = "select * from ((select * from test1) UNION (select * from test2) UNION (select * from test3)) where t1='温高铁';"; + + SQLStatementParser parser = new MySqlStatementParser(sql); + List stmtList = parser.parseStatementList(); + + output(stmtList); + } private void output(List stmtList) { for (SQLStatement stmt : stmtList) { diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/SQLUtilsTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/SQLUtilsTest.java index a376d3b0a6..430bca6bfc 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/SQLUtilsTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/SQLUtilsTest.java @@ -18,4 +18,15 @@ public class SQLUtilsTest extends TestCase { "\nFROM t" + // "\nWHERE id = 'abc'", formattedSql); } + + public void test_format_1() throws Exception { + String sql = "select * from t where tname LIKE \"%\"'温'\"%\""; + String formattedSql = SQLUtils.formatMySql(sql); + Assert.assertEquals("SELECT *\nFROM t\nWHERE tname LIKE CONCAT('%', '温', '%')", formattedSql); + } + + public void test_format_2() throws Exception { + String sql = "begin\n" + " if (a=10) then\n" + " null;\n" + " else\n" + " null;\n" + " end if;\n" + "end;"; + System.out.println(SQLUtils.formatOracle(sql)); + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/visitor/MySqlParameterizedOutputVisitorTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/visitor/MySqlParameterizedOutputVisitorTest.java new file mode 100644 index 0000000000..9dee025bc8 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/visitor/MySqlParameterizedOutputVisitorTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.mysql.visitor; + +import junit.framework.TestCase; + +import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; +import com.alibaba.druid.util.JdbcConstants; + +public class MySqlParameterizedOutputVisitorTest extends TestCase { + + public void test_0() throws Exception { + String sql = "SELECT appsheetserialno FROM app_trans WHERE nodeid = _gbk '619' " + + " AND alino = _gbk '2013110900031031001700thfund00163619'" + + " AND apserialno = _gbk '201405120002300002170013205458'"; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, "mysql")); + System.out.println("-----------------------"); + + sql = "SELECT p.AppId, p.PlayerId, p.Nickname, p.CreateTime FROM acc_playeruser AS pu INNER JOIN acc_player AS p ON (pu.AppId = p.AppId AND pu.PlayerId = p.PlayerId) WHERE pu.UserId=x'881A58F6204D4E048F66E41596A66A57';"; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, "mysql")); + System.out.println("-----------------------"); + + sql = "SET @now := NOW(); INSERT characters (uid, name, race, career, sex, creation) VALUES (4068548, '', 58, 0, 0, @now); SET @id := LAST_INSERT_ID(); INSERT characters_data (character_id, data, creation) VALUES (@id, ' \0\0\0\0\0\0\0\04068548\0\0\0:\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0<\0\0\0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0d\0\0\0__\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', @now); SELECT @id; "; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, "mysql")); + System.out.println("-----------------------"); + + sql = "INSERT INTO qrtz_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP, DESCRIPTION, JOB_CLASS_NAME, IS_DURABLE, IS_NONCONCURRENT, IS_UPDATE_DATA, REQUESTS_RECOVERY, JOB_DATA) VALUES('DefaultQuartzScheduler', 'taobao.item.recommend.delete_368815794_2_35391685928', 'tasks', null, 'cn.agooo.job.TasksJob', 0, 0, 0, 0, x'ACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000017400077461736B7349647372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000002063227800')"; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, "mysql")); + System.out.println("-----------------------"); + + sql = "SELECT * FROM sync_contactsdata WHERE __id=x'2EEE5AE7CB0E4AF697D966AE8BF046B8'"; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, "mysql")); + System.out.println("-----------------------"); + + sql = "select TID,POSTFEE,RECEIVERADDRESS from tradegroup003 where (0 = 650954981082695 or tid=650954981082695) And SELLERNICK='______________' And BUYERNICK='yingge7' and CREATED > date_sub(now(), interval 48 hour) and STATUS = 'WAIT_BUYER_PAY' and func_isNotFollowgroup003(tradegroup003.tid,'______________') = 0"; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, "mysql")); + System.out.println("-----------------------"); + + sql = "SHOW COLUMNS FROM `pms_purchase_ops`/*20140512152820##%2Fbase.php%3Fshopid%3D%26module%3Dpms%26action%3DqualityItem%26op_id%3D756%26params%3Dcase%253Aupdate%252Ctable%253Aquality%252Corder_id%253A201405090006DL%252Creceive_no%253AQCI01201405090006DL%252Creceive_treat%253AALL_DONE%260.5652460628381133*/;"; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, "mysql")); + System.out.println("-----------------------"); + } + + public void test_1() throws Exception { + String sql = "create or replace view vtmp_log_node_204180 as select `twf_log_group_user`.`uni_id` as `user_id`, `twf_log_group_user`.`control_group_type` from `twf_log_group_user` where `twf_log_group_user`.`subjob_id` = 204180"; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, JdbcConstants.MYSQL)); + System.out.println("-----------------------"); + + sql = " create view V_CustomerGoodsNf as select a.*,b.GoodsNO,b.GoodsName,b.SpecName,c.CustomerName,c.NickName,c.Tel,c.Email, StatusExt = (CASE a.curStatus WHEN (0) THEN ('待通知') WHEN (1) THEN ('已通知') WHEN (2) THEN ('已过期') WHEN (3) THEN ('被取消')END) from G_Customer_GoodsNotify a left outer join V_GoodsListBySpec b on a.GoodsID=b.GoodsID and a.SpecID=b.SpecID left outer join G_Customer_CustomerList c on a.CustomerID=c.CustomerID"; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, JdbcConstants.SQL_SERVER)); + System.out.println("-----------------------"); + + sql = "CREATE view V_API_GoodsMatch as SELECT a.SpecName, a.GoodsNO, a.GoodsName, bMatch = CONVERT(bit, (CASE isnull(b.Numiid, '') WHEN ('') THEN (0) ELSE (1) END)), issys1 = (CASE isnull(b.issys, '') WHEN ('') THEN ('无') WHEN (0) THEN ('未同步') WHEN (1) THEN ('待同步') WHEN (2) THEN ('同步失败') WHEN (3) THEN ('同步成功') END), bFixNum = isnull(b.bFixNum, 0), bVirNum = isnull(b.bVirNum, 0), issys = isnull(b.issys, 0), FixNum = isnull(b.FixNum, 0), VirNumBase = isnull(b.VirNumBase, 0), VirNumInc = isnull(b.VirNumInc, 0), b.TBName, b.TBSku, b.UpdateTime, b.syscount, b.sysLog, c.ShopName, b.TBOuterID, b.SKUOuterID, d .FlagName, b.GoodsID, b.SpecID, b.Numiid, b.Skuid, b.BTBGoods, b.sysGoodsType, b.ID, bstop = CONVERT(bit, isnull(b.bstop, 0)), b.bSingletb, b.SingleNumPer, b.VirNumTop, isnull(b.goodstype, '0') AS goodstype, CASE b.SpecID WHEN - 2 THEN 1 ELSE 0 END AS pcbs FROM V_GoodsSpec a LEFT OUTER JOIN G_API_SysMatch b ON a.GoodsID = b.GoodsID AND CASE b.SpecID WHEN - 2 THEN 1 ELSE a.specid END = CASE b.SpecID WHEN - 2 THEN 1 ELSE b.SpecID END AND b.GoodsType = 0 LEFT OUTER JOIN G_Cfg_ShopList c ON b.ShopID = c.ShopID LEFT OUTER JOIN dbo.G_Cfg_RecordFlag d ON a.FlagID = d .FlagID UNION ALL SELECT '' AS SpecName, a.GoodsNO, a.GoodsName, bMatch = CONVERT(bit, (CASE isnull(b.Numiid, '') WHEN ('') THEN (0) ELSE (1) END)), issys1 = (CASE isnull(b.issys, '') WHEN ('') THEN ('无') WHEN (0) THEN ('未同步') WHEN (1) THEN ('待同步') WHEN (2) THEN ('同步失败') WHEN (3) THEN ('同步成功') END), bFixNum = isnull(b.bFixNum, 0), bVirNum = isnull(b.bVirNum, 0), issys = isnull(b.issys, 0), FixNum = isnull(b.FixNum, 0), VirNumBase = isnull(b.VirNumBase, 0), VirNumInc = isnull(b.VirNumInc, 0), b.TBName, b.TBSku, b.UpdateTime, b.syscount, b.sysLog, c.ShopName, b.TBOuterID, b.SKUOuterID, '' AS FlagName, b.GoodsID, b.SpecID, b.Numiid, b.Skuid, b.BTBGoods, b.sysGoodsType, b.ID, bstop = CONVERT(bit, isnull(b.bstop, 0)), b.bSingletb, b.SingleNumPer, b.VirNumTop, b.goodstype, 0 AS pcbs FROM g_goods_goodslistfit a LEFT OUTER JOIN g_api_sysma"; + System.out.println(ParameterizedOutputVisitorUtils.parameterize(sql, JdbcConstants.SQL_SERVER)); + System.out.println("-----------------------"); + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleListAggTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleListAggTest.java new file mode 100644 index 0000000000..eb7a4520b3 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleListAggTest.java @@ -0,0 +1,62 @@ +package com.alibaba.druid.bvt.sql.oracle; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import com.alibaba.druid.util.JdbcConstants; + +public class OracleListAggTest extends OracleTest { + + public void test_0() throws Exception { + String sql = "SELECT prod_id, LISTAGG(cust_first_name||' '||cust_last_name, '; ') \n" // + + " WITHIN GROUP (ORDER BY amount_sold DESC) cust_list\n" // + + "FROM sales, customers\n" // + + "WHERE sales.cust_id = customers.cust_id AND cust_gender = 'M' \n" // + + " AND cust_credit_limit = 15000 AND prod_id BETWEEN 15 AND 18 \n" // + + " AND channel_id = 2 AND time_id > '01-JAN-01'\n" // + + "GROUP BY prod_id;"; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + Assert.assertEquals("SELECT prod_id, LISTAGG(cust_first_name || ' ' || cust_last_name, '; ') WITHIN GROUP (ORDER BY amount_sold DESC) AS cust_list" // + + "\nFROM sales, customers"// + + "\nWHERE sales.cust_id = customers.cust_id" // + + "\n\tAND cust_gender = 'M'" // + + "\n\tAND cust_credit_limit = 15000" // + + "\n\tAND prod_id BETWEEN 15 AND 18" // + + "\n\tAND channel_id = 2" // + + "\n\tAND time_id > '01-JAN-01'" // + + "\nGROUP BY prod_id",// + SQLUtils.toSQLString(stmt, JdbcConstants.ORACLE)); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + + Assert.assertEquals(2, visitor.getTables().size()); + Assert.assertEquals(10, visitor.getColumns().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("sales"))); + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("customers"))); + + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales", "cust_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("customers", "cust_id"))); + } + +} diff --git a/src/test/resources/bvt/parser/mysql-6.txt b/src/test/resources/bvt/parser/mysql-6.txt index 9045779040..cc155a9025 100644 --- a/src/test/resources/bvt/parser/mysql-6.txt +++ b/src/test/resources/bvt/parser/mysql-6.txt @@ -42,6 +42,14 @@ FROM (SELECT a.activity_offer_id AS adResourceId, a.title AS adResourceName, a.b , c.position_name AS positionName, c.charge_setting AS feeRegulation, a.priority_weight AS priorityWeight, a.audit_state AS adResourceState, 0 AS adResourceType , a.gmt_create, a.refuse_remark FROM q_activity_offer_enroll a, q_custinfo b, q_activity_position c + WHERE a.custid = b.custid + AND a.activity_position_id = c.activity_position_id + AND c.activity_id = ? + UNION ALL + SELECT a.reg_position_id AS adResourceId, b.custname AS adResourceName, NULL AS adResourceURL, a.custid, b.memberid + , c.position_name AS positionName, c.charge_setting AS feeRegulation, NULL AS priorityWeight, a.audit_state AS adResourceState, 1 AS adResourceType + , a.gmt_create, a.refuse_remark + FROM q_activity_custom_enroll a, q_custinfo b, q_activity_position c WHERE a.custid = b.custid AND a.activity_position_id = c.activity_position_id AND c.activity_id = ?