content: /*
   * Copyright 1999-2018 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.proxy.jdbc;
  
  import com.alibaba.druid.filter.FilterChainImpl;
  import com.alibaba.druid.stat.JdbcSqlStat;
  
  import java.sql.*;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
  
  /**
   * @author wenshao [szujobs@hotmail.com]
   */
  public class StatementProxyImpl extends WrapperProxyImpl implements StatementProxy {
      private final ConnectionProxy connection;
      protected Statement statement;
      protected String lastExecuteSql;
      protected long lastExecuteStartNano;
      protected long lastExecuteTimeNano;
      protected JdbcSqlStat sqlStat;
      protected boolean firstResultSet;
      protected ArrayList<String> batchSqlList;
      protected StatementExecuteType lastExecuteType;
      protected Integer updateCount;
      private FilterChainImpl filterChain;
  
      public StatementProxyImpl(ConnectionProxy connection, Statement statement, long id) {
          super(statement, id);
          this.connection = connection;
          this.statement = statement;
      }
  
      public ConnectionProxy getConnectionProxy() {
          return connection;
      }
  
      public Statement getRawObject() {
          return this.statement;
      }
  
      public final FilterChainImpl createChain() {
          FilterChainImpl chain = this.filterChain;
          if (chain == null) {
              chain = new FilterChainImpl(this.getConnectionProxy().getDirectDataSource());
          } else {
              this.filterChain = null;
          }
  
          return chain;
      }
  
      public final void recycleFilterChain(FilterChainImpl chain) {
          chain.reset();
          this.filterChain = chain;
      }
  
      @Override
      public void addBatch(String sql) throws SQLException {
          if (batchSqlList == null) {
              batchSqlList = new ArrayList<String>();
          }
  
          FilterChainImpl chain = createChain();
          chain.statement_addBatch(this, sql);
          recycleFilterChain(chain);
          batchSqlList.add(sql);
      }
  
      @Override
      public void cancel() throws SQLException {
          if (this.statement == null) {
              return;
          }
  
          FilterChainImpl chain = createChain();
          chain.statement_cancel(this);
          recycleFilterChain(chain);
      }
  
      @Override
      public void clearBatch() throws SQLException {
          if (this.statement == null) {
              return;
          }
  
          if (batchSqlList == null) {
              batchSqlList = new ArrayList<String>();
          }
  
          FilterChainImpl chain = createChain();
          chain.statement_clearBatch(this);
          recycleFilterChain(chain);
          batchSqlList.clear();
      }
  
      @Override
      public void clearWarnings() throws SQLException {
          if (this.statement == null) {
              return;
          }
  
          FilterChainImpl chain = createChain();
          chain.statement_clearWarnings(this);
          recycleFilterChain(chain);
      }
  
      @Override
      public void close() throws SQLException {
          if (this.statement == null) {
              return;
          }
  
          FilterChainImpl chain = createChain();
          chain.statement_close(this);
          recycleFilterChain(chain);
      }
  
      @Override
      public boolean execute(String sql) throws SQLException {
          updateCount = null;
          lastExecuteSql = sql;
          lastExecuteType = StatementExecuteType.Execute;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          firstResultSet = chain.statement_execute(this, sql);
          recycleFilterChain(chain);
          return firstResultSet;
      }
  
      @Override
      public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
          updateCount = null;
          lastExecuteSql = sql;
          lastExecuteType = StatementExecuteType.Execute;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          firstResultSet = chain.statement_execute(this, sql, autoGeneratedKeys);
          recycleFilterChain(chain);
          return firstResultSet;
      }
  
      @Override
      public boolean execute(String sql, int[] columnIndexes) throws SQLException {
          updateCount = null;
          lastExecuteSql = sql;
          lastExecuteType = StatementExecuteType.Execute;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          firstResultSet = chain.statement_execute(this, sql, columnIndexes);
          recycleFilterChain(chain);
          return firstResultSet;
      }
  
      @Override
      public boolean execute(String sql, String[] columnNames) throws SQLException {
          updateCount = null;
          lastExecuteSql = sql;
          lastExecuteType = StatementExecuteType.Execute;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          firstResultSet = chain.statement_execute(this, sql, columnNames);
          recycleFilterChain(chain);
          return firstResultSet;
      }
  
      @Override
      public int[] executeBatch() throws SQLException {
          firstResultSet = false;
          lastExecuteType = StatementExecuteType.ExecuteBatch;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          int[] updateCounts = chain.statement_executeBatch(this);
          recycleFilterChain(chain);
          if (updateCounts != null && updateCounts.length == 1) {
              updateCount = updateCounts[0];
          }
  
          return updateCounts;
      }
  
      @Override
      public ResultSet executeQuery(String sql) throws SQLException {
          firstResultSet = true;
          updateCount = null;
          lastExecuteSql = sql;
          lastExecuteType = StatementExecuteType.ExecuteQuery;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          ResultSet resultSet = chain.statement_executeQuery(this, sql);
          recycleFilterChain(chain);
          return resultSet;
      }
  
      @Override
      public int executeUpdate(String sql) throws SQLException {
          firstResultSet = false;
          lastExecuteSql = sql;
          lastExecuteType = StatementExecuteType.ExecuteUpdate;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          updateCount = chain.statement_executeUpdate(this, sql);
          recycleFilterChain(chain);
          return updateCount;
      }
  
      @Override
      public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
          firstResultSet = false;
          lastExecuteSql = sql;
          lastExecuteType = StatementExecuteType.ExecuteUpdate;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          updateCount = chain.statement_executeUpdate(this, sql, autoGeneratedKeys);
          recycleFilterChain(chain);
          return updateCount;
      }
  
      @Override
      public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
          firstResultSet = false;
          lastExecuteSql = sql;
          lastExecuteType = StatementExecuteType.ExecuteUpdate;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          updateCount = chain.statement_executeUpdate(this, sql, columnIndexes);
          recycleFilterChain(chain);
          return updateCount;
      }
  
      @Override
      public int executeUpdate(String sql, String[] columnNames) throws SQLException {
          firstResultSet = false;
          lastExecuteSql = sql;
          lastExecuteType = StatementExecuteType.ExecuteUpdate;
          lastExecuteStartNano = -1L;
          lastExecuteTimeNano = -1L;
  
          FilterChainImpl chain = createChain();
          updateCount = chain.statement_executeUpdate(this, sql, columnNames);
          recycleFilterChain(chain);
          return updateCount;
      }
  
      @Override
      public Connection getConnection() throws SQLException {
          FilterChainImpl chain = createChain();
          Connection conn = chain.statement_getConnection(this);
          recycleFilterChain(chain);
          return conn;
      }
  
      @Override
      public int getFetchDirection() throws SQLException {
          FilterChainImpl chain = createChain();
          int value = chain.statement_getFetchDirection(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public int getFetchSize() throws SQLException {
          FilterChainImpl chain = createChain();
          int value = chain.statement_getFetchSize(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public ResultSet getGeneratedKeys() throws SQLException {
          FilterChainImpl chain = createChain();
          ResultSet value = chain.statement_getGeneratedKeys(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public int getMaxFieldSize() throws SQLException {
          FilterChainImpl chain = createChain();
          int value = chain.statement_getMaxFieldSize(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public int getMaxRows() throws SQLException {
          FilterChainImpl chain = createChain();
          int value = chain.statement_getMaxRows(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public boolean getMoreResults() throws SQLException {
          FilterChainImpl chain = createChain();
          boolean value = chain.statement_getMoreResults(this);
          updateCount = null;
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public boolean getMoreResults(int current) throws SQLException {
          updateCount = null;
          FilterChainImpl chain = createChain();
          boolean value = chain.statement_getMoreResults(this, current);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public int getQueryTimeout() throws SQLException {
          FilterChainImpl chain = createChain();
          int value = chain.statement_getQueryTimeout(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public ResultSet getResultSet() throws SQLException {
          FilterChainImpl chain = createChain();
          ResultSet value = chain.statement_getResultSet(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public int getResultSetConcurrency() throws SQLException {
          FilterChainImpl chain = createChain();
          int value = chain.statement_getResultSetConcurrency(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public int getResultSetHoldability() throws SQLException {
          FilterChainImpl chain = createChain();
          int value = chain.statement_getResultSetHoldability(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public int getResultSetType() throws SQLException {
          FilterChainImpl chain = createChain();
          int value = chain.statement_getResultSetType(this);
          recycleFilterChain(chain);
          return value;
      }
  
      // bug fixed for oracle
      @Override
      public int getUpdateCount() throws SQLException {
          if (updateCount == null) {
              FilterChainImpl chain = createChain();
              updateCount = chain.statement_getUpdateCount(this);
              recycleFilterChain(chain);
          }
          return updateCount;
      }
  
      @Override
      public SQLWarning getWarnings() throws SQLException {
          FilterChainImpl chain = createChain();
          SQLWarning value = chain.statement_getWarnings(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public boolean isClosed() throws SQLException {
          FilterChainImpl chain = createChain();
          boolean value = chain.statement_isClosed(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public boolean isPoolable() throws SQLException {
          FilterChainImpl chain = createChain();
          boolean value = chain.statement_isPoolable(this);
          recycleFilterChain(chain);
          return value;
      }
  
      @Override
      public void setCursorName(String name) throws SQLException {
          FilterChainImpl chain = createChain();
          chain.statement_setCursorName(this, name);
          recycleFilterChain(chain);
      }
  
      @Override
      public void setEscapeProcessing(boolean enable) throws SQLException {
          FilterChainImpl chain = createChain();
          chain.statement_setEscapeProcessing(this, enable);
          recycleFilterChain(chain);
      }
  
      @Override
      public void setFetchDirection(int direction) throws SQLException {
          FilterChainImpl chain = createChain();
          chain.statement_setFetchDirection(this, direction);
          recycleFilterChain(chain);
      }
  
      @Override
      public void setFetchSize(int rows) throws SQLException {
          FilterChainImpl chain = createChain();
          chain.statement_setFetchSize(this, rows);
          recycleFilterChain(chain);
      }
  
      @Override
      public void setMaxFieldSize(int max) throws SQLException {
          FilterChainImpl chain = createChain();
          chain.statement_setMaxFieldSize(this, max);
          recycleFilterChain(chain);
      }
  
      @Override
      public void setMaxRows(int max) throws SQLException {
          FilterChainImpl chain = createChain();
          chain.statement_setMaxRows(this, max);
          recycleFilterChain(chain);
      }
  
      @Override
      public void setPoolable(boolean poolable) throws SQLException {
          FilterChainImpl chain = createChain();
          chain.statement_setPoolable(this, poolable);
          recycleFilterChain(chain);
      }
  
      @Override
      public void setQueryTimeout(int seconds) throws SQLException {
          FilterChainImpl chain = createChain();
          chain.statement_setQueryTimeout(this, seconds);
          recycleFilterChain(chain);
      }
  
      @Override
      public List<String> getBatchSqlList() {
          if (batchSqlList == null) {
              batchSqlList = new ArrayList<String>();
          }
  
          return batchSqlList;
      }
  
      @Override
      public String getBatchSql() {
          List<String> sqlList = getBatchSqlList();
          StringBuilder buf = new StringBuilder();
          for (String item : sqlList) {
              if (buf.length() > 0) {
                  buf.append("\n;\n");
              }
              buf.append(item);
          }
          return buf.toString();
      }
  
      public String getLastExecuteSql() {
          return lastExecuteSql;
      }
  
      public void closeOnCompletion() throws SQLException {
          statement.closeOnCompletion();
      }
  
      public boolean isCloseOnCompletion() throws SQLException {
          return statement.isCloseOnCompletion();
      }
  
      @Override
      public Map<Integer, JdbcParameter> getParameters() {
          return Collections.emptyMap();
      }
  
      public JdbcSqlStat getSqlStat() {
          return sqlStat;
      }
  
      public void setSqlStat(JdbcSqlStat sqlStat) {
          this.sqlStat = sqlStat;
      }
  
      public long getLastExecuteTimeNano() {
          return lastExecuteTimeNano;
      }
  
      public void setLastExecuteTimeNano(long lastExecuteTimeNano) {
          this.lastExecuteTimeNano = lastExecuteTimeNano;
      }
  
      public void setLastExecuteTimeNano() {
          if (this.lastExecuteTimeNano <= 0 && this.lastExecuteStartNano > 0) {
              this.lastExecuteTimeNano = System.nanoTime() - this.lastExecuteStartNano;
          }
      }
  
      public long getLastExecuteStartNano() {
          return lastExecuteStartNano;
      }
  
      public void setLastExecuteStartNano(long lastExecuteStartNano) {
          this.lastExecuteStartNano = lastExecuteStartNano;
          this.lastExecuteTimeNano = -1L;
      }
  
      public void setLastExecuteStartNano() {
          if (lastExecuteStartNano <= 0) {
              setLastExecuteStartNano(System.nanoTime());
          }
      }
  
      public StatementExecuteType getLastExecuteType() {
          return lastExecuteType;
      }
  
      public boolean isFirstResultSet() {
          return firstResultSet;
      }
  
      @SuppressWarnings("unchecked")
      public <T> T unwrap(Class<T> iface) throws SQLException {
          if (iface == StatementProxy.class) {
              return (T) this;
          }
  
          return super.unwrap(iface);
      }
  
      public boolean isWrapperFor(Class<?> iface) throws SQLException {
          if (iface == StatementProxy.class) {
              return true;
          }
  
          return super.isWrapperFor(iface);
      }
  
      @Override
      public int getParametersSize() {
          return 0;
      }
  
      @Override
      public JdbcParameter getParameter(int i) {
          return null;
      }
  }
  