/*
 * 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.stat;

import com.alibaba.druid.proxy.DruidDriver;
import com.alibaba.druid.proxy.jdbc.StatementExecuteType;
import com.alibaba.druid.util.JMXUtils;
import com.alibaba.druid.util.Utils;

import javax.management.JMException;
import javax.management.openmbean.*;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;

import static com.alibaba.druid.util.JdbcSqlStatUtils.get;

public final class JdbcSqlStat implements JdbcSqlStatMBean, Comparable<JdbcSqlStat> {
    private final String sql;
    private long sqlHash;
    private long id;
    private String dataSource;
    private long executeLastStartTime;

    private volatile long executeBatchSizeTotal;
    private volatile int executeBatchSizeMax;

    private volatile long executeSuccessCount;
    private volatile long executeSpanNanoTotal;
    private volatile long executeSpanNanoMax;
    private volatile int runningCount;
    private volatile int concurrentMax;
    private volatile long resultSetHoldTimeNano;
    private volatile long executeAndResultSetHoldTime;

    static final AtomicLongFieldUpdater<JdbcSqlStat> executeBatchSizeTotalUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeBatchSizeTotal");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> executeBatchSizeMaxUpdater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeBatchSizeMax");

    static final AtomicLongFieldUpdater<JdbcSqlStat> executeSuccessCountUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeSuccessCount");
    static final AtomicLongFieldUpdater<JdbcSqlStat> executeSpanNanoTotalUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeSpanNanoTotal");
    static final AtomicLongFieldUpdater<JdbcSqlStat> executeSpanNanoMaxUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeSpanNanoMax");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> runningCountUpdater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "runningCount");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> concurrentMaxUpdater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "concurrentMax");
    static final AtomicLongFieldUpdater<JdbcSqlStat> resultSetHoldTimeNanoUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "resultSetHoldTimeNano");
    static final AtomicLongFieldUpdater<JdbcSqlStat> executeAndResultSetHoldTimeUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeAndResultSetHoldTime");

    private String name;
    private String file;
    private String dbType;

    private volatile long executeNanoSpanMaxOccurTime;

    private volatile long executeErrorCount;
    private volatile Throwable executeErrorLast;
    private volatile long executeErrorLastTime;

    private volatile long updateCount;
    private volatile long updateCountMax;
    private volatile long fetchRowCount;
    private volatile long fetchRowCountMax;

    private volatile long inTransactionCount;

    private volatile String lastSlowParameters;

    private boolean removed;

    private volatile long clobOpenCount;
    private volatile long blobOpenCount;
    private volatile long readStringLength;
    private volatile long readBytesLength;

    private volatile long inputStreamOpenCount;
    private volatile long readerOpenCount;

    static final AtomicLongFieldUpdater<JdbcSqlStat> executeErrorCountUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeErrorCount");
    static final AtomicLongFieldUpdater<JdbcSqlStat> updateCountUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "updateCount");
    static final AtomicLongFieldUpdater<JdbcSqlStat> updateCountMaxUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "updateCountMax");
    static final AtomicLongFieldUpdater<JdbcSqlStat> fetchRowCountUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "fetchRowCount");
    static final AtomicLongFieldUpdater<JdbcSqlStat> fetchRowCountMaxUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "fetchRowCountMax");
    static final AtomicLongFieldUpdater<JdbcSqlStat> inTransactionCountUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "inTransactionCount");

    static final AtomicLongFieldUpdater<JdbcSqlStat> clobOpenCountUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "clobOpenCount");
    static final AtomicLongFieldUpdater<JdbcSqlStat> blobOpenCountUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "blobOpenCount");
    static final AtomicLongFieldUpdater<JdbcSqlStat> readStringLengthUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "readStringLength");
    static final AtomicLongFieldUpdater<JdbcSqlStat> readBytesLengthUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "readBytesLength");

    static final AtomicLongFieldUpdater<JdbcSqlStat> inputStreamOpenCountUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "inputStreamOpenCount");
    static final AtomicLongFieldUpdater<JdbcSqlStat> readerOpenCountUpdater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "readerOpenCount");

    private volatile long histogram_0_1;
    private volatile long histogram_1_10;
    private volatile int histogram_10_100;
    private volatile int histogram_100_1000;
    private volatile int histogram_1000_10000;
    private volatile int histogram_10000_100000;
    private volatile int histogram_100000_1000000;
    private volatile int histogram_1000000_more;

    static final AtomicLongFieldUpdater<JdbcSqlStat> histogram_0_1_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "histogram_0_1");
    static final AtomicLongFieldUpdater<JdbcSqlStat> histogram_1_10_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "histogram_1_10");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> histogram_10_100_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "histogram_10_100");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> histogram_100_1000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "histogram_100_1000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> histogram_1000_10000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "histogram_1000_10000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> histogram_10000_100000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "histogram_10000_100000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> histogram_100000_1000000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "histogram_100000_1000000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> histogram_1000000_more_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "histogram_1000000_more");

    private volatile long executeAndResultHoldTime_0_1;
    private volatile long executeAndResultHoldTime_1_10;
    private volatile int executeAndResultHoldTime_10_100;
    private volatile int executeAndResultHoldTime_100_1000;
    private volatile int executeAndResultHoldTime_1000_10000;
    private volatile int executeAndResultHoldTime_10000_100000;
    private volatile int executeAndResultHoldTime_100000_1000000;
    private volatile int executeAndResultHoldTime_1000000_more;

    static final AtomicLongFieldUpdater<JdbcSqlStat> executeAndResultHoldTime_0_1_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeAndResultHoldTime_0_1");
    static final AtomicLongFieldUpdater<JdbcSqlStat> executeAndResultHoldTime_1_10_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeAndResultHoldTime_1_10");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> executeAndResultHoldTime_10_100_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeAndResultHoldTime_10_100");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> executeAndResultHoldTime_100_1000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeAndResultHoldTime_100_1000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> executeAndResultHoldTime_1000_10000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeAndResultHoldTime_1000_10000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> executeAndResultHoldTime_10000_100000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeAndResultHoldTime_10000_100000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> executeAndResultHoldTime_100000_1000000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeAndResultHoldTime_100000_1000000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> executeAndResultHoldTime_1000000_more_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "executeAndResultHoldTime_1000000_more");

    private volatile long fetchRowCount_0_1;
    private volatile long fetchRowCount_1_10;
    private volatile long fetchRowCount_10_100;
    private volatile int fetchRowCount_100_1000;
    private volatile int fetchRowCount_1000_10000;
    private volatile int fetchRowCount_10000_more;

    static final AtomicLongFieldUpdater<JdbcSqlStat> fetchRowCount_0_1_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "fetchRowCount_0_1");
    static final AtomicLongFieldUpdater<JdbcSqlStat> fetchRowCount_1_10_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "fetchRowCount_1_10");
    static final AtomicLongFieldUpdater<JdbcSqlStat> fetchRowCount_10_100_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "fetchRowCount_10_100");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> fetchRowCount_100_1000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "fetchRowCount_100_1000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> fetchRowCount_1000_10000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "fetchRowCount_1000_10000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> fetchRowCount_10000_more_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "fetchRowCount_10000_more");

    private volatile long updateCount_0_1;
    private volatile long updateCount_1_10;
    private volatile long updateCount_10_100;
    private volatile int updateCount_100_1000;
    private volatile int updateCount_1000_10000;
    private volatile int updateCount_10000_more;

    static final AtomicLongFieldUpdater<JdbcSqlStat> updateCount_0_1_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "updateCount_0_1");
    static final AtomicLongFieldUpdater<JdbcSqlStat> updateCount_1_10_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "updateCount_1_10");
    static final AtomicLongFieldUpdater<JdbcSqlStat> updateCount_10_100_Updater = AtomicLongFieldUpdater.newUpdater(JdbcSqlStat.class,
            "updateCount_10_100");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> updateCount_100_1000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "updateCount_100_1000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> updateCount_1000_10000_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "updateCount_1000_10000");
    static final AtomicIntegerFieldUpdater<JdbcSqlStat> updateCount_10000_more_Updater = AtomicIntegerFieldUpdater.newUpdater(JdbcSqlStat.class,
            "updateCount_10000_more");

    public JdbcSqlStat(String sql) {
        this.sql = sql;
        this.id = DruidDriver.createSqlStatId();
    }

    public String getLastSlowParameters() {
        return lastSlowParameters;
    }

    public void setLastSlowParameters(String lastSlowParameters) {
        this.lastSlowParameters = lastSlowParameters;
    }

    public String getDbType() {
        return dbType;
    }

    public void setDbType(String dbType) {
        this.dbType = dbType;
    }

    public String getDataSource() {
        return dataSource;
    }

    public void setDataSource(String dataSource) {
        this.dataSource = dataSource;
    }

    public static final String getContextSqlName() {
        JdbcStatContext context = JdbcStatManager.getInstance().getStatContext();
        if (context == null) {
            return null;
        }
        return context.getName();
    }

    public static final void setContextSqlName(String val) {
        JdbcStatContext context = JdbcStatManager.getInstance().getStatContext();
        if (context == null) {
            context = JdbcStatManager.getInstance().createStatContext();
            JdbcStatManager.getInstance().setStatContext(context);
        }

        context.setName(val);
    }

    public static final String getContextSqlFile() {
        JdbcStatContext context = JdbcStatManager.getInstance().getStatContext();
        if (context == null) {
            return null;
        }
        return context.getFile();
    }

    public static final void setContextSqlFile(String val) {
        JdbcStatContext context = JdbcStatManager.getInstance().getStatContext();
        if (context == null) {
            context = JdbcStatManager.getInstance().createStatContext();
            JdbcStatManager.getInstance().setStatContext(context);
        }

        context.setFile(val);
    }

    public static final void setContextSql(String val) {
        JdbcStatContext context = JdbcStatManager.getInstance().getStatContext();
        if (context == null) {
            context = JdbcStatManager.getInstance().createStatContext();
            JdbcStatManager.getInstance().setStatContext(context);
        }

        context.setSql(val);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getFile() {
        return file;
    }

    public void setFile(String file) {
        this.file = file;
    }

    public void reset() {
        executeLastStartTime = 0;

        executeBatchSizeTotalUpdater.set(this, 0);
        executeBatchSizeMaxUpdater.set(this, 0);

        executeSuccessCountUpdater.set(this, 0);
        executeSpanNanoTotalUpdater.set(this, 0);
        executeSpanNanoMaxUpdater.set(this, 0);
        executeNanoSpanMaxOccurTime = 0;
        concurrentMaxUpdater.set(this, 0);

        executeErrorCountUpdater.set(this, 0);
        executeErrorLast = null;
        executeErrorLastTime = 0;

        updateCountUpdater.set(this, 0);
        updateCountMaxUpdater.set(this, 0);
        fetchRowCountUpdater.set(this, 0);
        fetchRowCountMaxUpdater.set(this, 0);

        histogram_0_1_Updater.set(this, 0);
        histogram_1_10_Updater.set(this, 0);
        histogram_10_100_Updater.set(this, 0);
        histogram_100_1000_Updater.set(this, 0);
        histogram_1000_10000_Updater.set(this, 0);
        histogram_10000_100000_Updater.set(this, 0);
        histogram_100000_1000000_Updater.set(this, 0);
        histogram_1000000_more_Updater.set(this, 0);

        this.lastSlowParameters = null;
        inTransactionCountUpdater.set(this, 0);
        resultSetHoldTimeNanoUpdater.set(this, 0);
        executeAndResultSetHoldTimeUpdater.set(this, 0);

        fetchRowCount_0_1_Updater.set(this, 0);
        fetchRowCount_1_10_Updater.set(this, 0);
        fetchRowCount_10_100_Updater.set(this, 0);
        fetchRowCount_100_1000_Updater.set(this, 0);
        fetchRowCount_1000_10000_Updater.set(this, 0);
        fetchRowCount_10000_more_Updater.set(this, 0);

        updateCount_0_1_Updater.set(this, 0);
        updateCount_1_10_Updater.set(this, 0);
        updateCount_10_100_Updater.set(this, 0);
        updateCount_100_1000_Updater.set(this, 0);
        updateCount_1000_10000_Updater.set(this, 0);
        updateCount_10000_more_Updater.set(this, 0);

        executeAndResultHoldTime_0_1_Updater.set(this, 0);
        executeAndResultHoldTime_1_10_Updater.set(this, 0);
        executeAndResultHoldTime_10_100_Updater.set(this, 0);
        executeAndResultHoldTime_100_1000_Updater.set(this, 0);
        executeAndResultHoldTime_1000_10000_Updater.set(this, 0);
        executeAndResultHoldTime_10000_100000_Updater.set(this, 0);
        executeAndResultHoldTime_100000_1000000_Updater.set(this, 0);
        executeAndResultHoldTime_1000000_more_Updater.set(this, 0);

        blobOpenCountUpdater.set(this, 0);
        clobOpenCountUpdater.set(this, 0);
        readStringLengthUpdater.set(this, 0);
        readBytesLengthUpdater.set(this, 0);
        inputStreamOpenCountUpdater.set(this, 0);
        readerOpenCountUpdater.set(this, 0);
    }

    public JdbcSqlStatValue getValueAndReset() {
        return getValue(true);
    }

    public JdbcSqlStatValue getValue(boolean reset) {
        JdbcSqlStatValue val = new JdbcSqlStatValue();

        val.setDbType(dbType);
        val.setSql(sql);
        val.setSqlHash(getSqlHash());
        val.setId(id);
        val.setName(name);
        val.setFile(file);
        val.setExecuteLastStartTime(executeLastStartTime);
        if (reset) {
            executeLastStartTime = 0;
        }

        val.setExecuteBatchSizeTotal(get(this, executeBatchSizeTotalUpdater, reset));
        val.setExecuteBatchSizeMax(get(this, executeBatchSizeMaxUpdater, reset));

        val.setExecuteSuccessCount(get(this, executeSuccessCountUpdater, reset));
        val.setExecuteSpanNanoTotal(get(this, executeSpanNanoTotalUpdater, reset));
        val.setExecuteSpanNanoMax(get(this, executeSpanNanoMaxUpdater, reset));
        val.setExecuteNanoSpanMaxOccurTime(executeNanoSpanMaxOccurTime);
        if (reset) {
            executeNanoSpanMaxOccurTime = 0;
        }

        val.setRunningCount(this.runningCount);

        val.setConcurrentMax(get(this, concurrentMaxUpdater, reset));

        val.setExecuteErrorCount(get(this, executeErrorCountUpdater, reset));

        val.setExecuteErrorLast(executeErrorLast);
        if (reset) {
            executeErrorLast = null;
        }

        val.setExecuteErrorLastTime(executeErrorLastTime);
        if (reset) {
            executeErrorLastTime = 0;
        }

        val.setUpdateCount(get(this, updateCountUpdater, reset));
        val.setUpdateCountMax(get(this, updateCountMaxUpdater, reset));
        val.setFetchRowCount(get(this, fetchRowCountUpdater, reset));
        val.setFetchRowCountMax(get(this, fetchRowCountMaxUpdater, reset));

        val.histogram_0_1 = get(this, histogram_0_1_Updater, reset);
        val.histogram_1_10 = get(this, histogram_1_10_Updater, reset);
        val.histogram_10_100 = get(this, histogram_10_100_Updater, reset);
        val.histogram_100_1000 = get(this, histogram_100_1000_Updater, reset);
        val.histogram_1000_10000 = get(this, histogram_1000_10000_Updater, reset);
        val.histogram_10000_100000 = get(this, histogram_10000_100000_Updater, reset);
        val.histogram_100000_1000000 = get(this, histogram_100000_1000000_Updater, reset);
        val.histogram_1000000_more = get(this, histogram_1000000_more_Updater, reset);

        val.setLastSlowParameters(lastSlowParameters);
        if (reset) {
            lastSlowParameters = null;
        }

        val.setInTransactionCount(get(this, inTransactionCountUpdater, reset));
        val.setResultSetHoldTimeNano(get(this, resultSetHoldTimeNanoUpdater, reset));
        val.setExecuteAndResultSetHoldTime(get(this, executeAndResultSetHoldTimeUpdater, reset));

        val.fetchRowCount_0_1 = get(this, fetchRowCount_0_1_Updater, reset);
        val.fetchRowCount_1_10 = get(this, fetchRowCount_1_10_Updater, reset);
        val.fetchRowCount_10_100 = get(this, fetchRowCount_10_100_Updater, reset);
        val.fetchRowCount_100_1000 = get(this, fetchRowCount_100_1000_Updater, reset);
        val.fetchRowCount_1000_10000 = get(this, fetchRowCount_1000_10000_Updater, reset);
        val.fetchRowCount_10000_more = get(this, fetchRowCount_10000_more_Updater, reset);

        val.updateCount_0_1 = get(this, updateCount_0_1_Updater, reset);
        val.updateCount_1_10 = get(this, updateCount_1_10_Updater, reset);
        val.updateCount_10_100 = get(this, updateCount_10_100_Updater, reset);
        val.updateCount_100_1000 = get(this, updateCount_100_1000_Updater, reset);
        val.updateCount_1000_10000 = get(this, updateCount_1000_10000_Updater, reset);
        val.updateCount_10000_more = get(this, updateCount_10000_more_Updater, reset);

        val.executeAndResultHoldTime_0_1 = get(this, executeAndResultHoldTime_0_1_Updater, reset);
        val.executeAndResultHoldTime_1_10 = get(this, executeAndResultHoldTime_1_10_Updater, reset);
        val.executeAndResultHoldTime_10_100 = get(this, executeAndResultHoldTime_10_100_Updater, reset);
        val.executeAndResultHoldTime_100_1000 = get(this, executeAndResultHoldTime_100_1000_Updater, reset);
        val.executeAndResultHoldTime_1000_10000 = get(this, executeAndResultHoldTime_1000_10000_Updater, reset);
        val.executeAndResultHoldTime_10000_100000 = get(this, executeAndResultHoldTime_10000_100000_Updater, reset);
        val.executeAndResultHoldTime_100000_1000000 = get(this, executeAndResultHoldTime_100000_1000000_Updater, reset);
        val.executeAndResultHoldTime_1000000_more = get(this, executeAndResultHoldTime_1000000_more_Updater, reset);

        val.setBlobOpenCount(get(this, blobOpenCountUpdater, reset));
        val.setClobOpenCount(get(this, clobOpenCountUpdater, reset));
        val.setReadStringLength(get(this, readStringLengthUpdater, reset));
        val.setReadBytesLength(get(this, readBytesLengthUpdater, reset));
        val.setInputStreamOpenCount(get(this, inputStreamOpenCountUpdater, reset));
        val.setReaderOpenCount(get(this, readerOpenCountUpdater, reset));

        return val;
    }

    public long getConcurrentMax() {
        return concurrentMax;
    }

    public long getRunningCount() {
        return runningCount;
    }

    public void addUpdateCount(int delta) {
        if (delta > 0) {
            updateCountUpdater.addAndGet(this, delta);
        }
        for (; ; ) {
            long max = updateCountMaxUpdater.get(this);
            if (delta <= max) {
                break;
            }
            if (updateCountMaxUpdater.compareAndSet(this, max, delta)) {
                break;
            }
        }

        if (delta < 1) {
            updateCount_0_1_Updater.incrementAndGet(this);
        } else if (delta < 10) {
            updateCount_1_10_Updater.incrementAndGet(this);
        } else if (delta < 100) {
            updateCount_10_100_Updater.incrementAndGet(this);
        } else if (delta < 1000) {
            updateCount_100_1000_Updater.incrementAndGet(this);
        } else if (delta < 10000) {
            updateCount_1000_10000_Updater.incrementAndGet(this);
        } else {
            updateCount_10000_more_Updater.incrementAndGet(this);
        }
    }

    public long getUpdateCount() {
        return updateCount;
    }

    public long getUpdateCountMax() {
        return updateCountMax;
    }

    public long getFetchRowCount() {
        return fetchRowCount;
    }

    public long getFetchRowCountMax() {
        return fetchRowCountMax;
    }

    public long getClobOpenCount() {
        return clobOpenCount;
    }

    public void incrementClobOpenCount() {
        clobOpenCountUpdater.incrementAndGet(this);
    }

    public long getBlobOpenCount() {
        return blobOpenCount;
    }

    public void incrementBlobOpenCount() {
        blobOpenCountUpdater.incrementAndGet(this);
    }

    public long getReadStringLength() {
        return readStringLength;
    }

    public void addStringReadLength(long length) {
        readStringLengthUpdater.addAndGet(this, length);
    }

    public long getReadBytesLength() {
        return readBytesLength;
    }

    public void addReadBytesLength(long length) {
        readBytesLengthUpdater.addAndGet(this, length);
    }

    public long getReaderOpenCount() {
        return readerOpenCount;
    }

    public void addReaderOpenCount(int count) {
        readerOpenCountUpdater.addAndGet(this, count);
    }

    public long getInputStreamOpenCount() {
        return inputStreamOpenCount;
    }

    public void addInputStreamOpenCount(int count) {
        inputStreamOpenCountUpdater.addAndGet(this, count);
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getSql() {
        return sql;
    }

    public long getSqlHash() {
        if (sqlHash == 0) {
            sqlHash = Utils.fnv_64(sql);
        }
        return sqlHash;
    }

    public Date getExecuteLastStartTime() {
        if (executeLastStartTime <= 0) {
            return null;
        }

        return new Date(executeLastStartTime);
    }

    public void setExecuteLastStartTime(long executeLastStartTime) {
        this.executeLastStartTime = executeLastStartTime;
    }

    public Date getExecuteNanoSpanMaxOccurTime() {
        if (executeNanoSpanMaxOccurTime <= 0) {
            return null;
        }
        return new Date(executeNanoSpanMaxOccurTime);
    }

    public Date getExecuteErrorLastTime() {
        if (executeErrorLastTime <= 0) {
            return null;
        }
        return new Date(executeErrorLastTime);
    }

    public void addFetchRowCount(long delta) {
        fetchRowCountUpdater.addAndGet(this, delta);
        for (; ; ) {
            long max = fetchRowCountMaxUpdater.get(this);
            if (delta <= max) {
                break;
            }
            if (fetchRowCountMaxUpdater.compareAndSet(this, max, delta)) {
                break;
            }
        }

        if (delta < 1) {
            fetchRowCount_0_1_Updater.incrementAndGet(this);
        } else if (delta < 10) {
            fetchRowCount_1_10_Updater.incrementAndGet(this);
        } else if (delta < 100) {
            fetchRowCount_10_100_Updater.incrementAndGet(this);
        } else if (delta < 1000) {
            fetchRowCount_100_1000_Updater.incrementAndGet(this);
        } else if (delta < 10000) {
            fetchRowCount_1000_10000_Updater.incrementAndGet(this);
        } else {
            fetchRowCount_10000_more_Updater.incrementAndGet(this);
        }

    }

    public void addExecuteBatchCount(long batchSize) {
        executeBatchSizeTotalUpdater.addAndGet(this, batchSize);

        // executeBatchSizeMax
        for (; ; ) {
            int current = executeBatchSizeMaxUpdater.get(this);
            if (current >= batchSize) {
                break;
            }

            if (executeBatchSizeMaxUpdater.compareAndSet(this, current, (int) batchSize)) {
                break;
            }
        }
    }

    public long getExecuteBatchSizeTotal() {
        return executeBatchSizeTotal;
    }

    public void incrementExecuteSuccessCount() {
        executeSuccessCountUpdater.incrementAndGet(this);
    }

    public void incrementRunningCount() {
        int val = runningCountUpdater.incrementAndGet(this);

        for (; ; ) {
            int max = concurrentMaxUpdater.get(this);
            if (val <= max) {
                break;
            }

            if (concurrentMaxUpdater.compareAndSet(this, max, val)) {
                break;
            }
        }
    }

    public void decrementRunningCount() {
        runningCountUpdater.decrementAndGet(this);
    }

    public void decrementExecutingCount() {
        runningCountUpdater.decrementAndGet(this);
    }

    public long getExecuteSuccessCount() {
        return executeSuccessCount;
    }

    public void addExecuteTime(StatementExecuteType executeType, boolean firstResultSet, long nanoSpan) {
        addExecuteTime(nanoSpan);

        if (StatementExecuteType.ExecuteQuery != executeType && !firstResultSet) {
            executeAndResultHoldTimeHistogramRecord(nanoSpan);
        }
    }

    private void executeAndResultHoldTimeHistogramRecord(long nanoSpan) {
        long millis = nanoSpan / 1000 / 1000;

        if (millis < 1) {
            executeAndResultHoldTime_0_1_Updater.incrementAndGet(this);
        } else if (millis < 10) {
            executeAndResultHoldTime_1_10_Updater.incrementAndGet(this);
        } else if (millis < 100) {
            executeAndResultHoldTime_10_100_Updater.incrementAndGet(this);
        } else if (millis < 1000) {
            executeAndResultHoldTime_100_1000_Updater.incrementAndGet(this);
        } else if (millis < 10000) {
            executeAndResultHoldTime_1000_10000_Updater.incrementAndGet(this);
        } else if (millis < 100000) {
            executeAndResultHoldTime_10000_100000_Updater.incrementAndGet(this);
        } else if (millis < 1000000) {
            executeAndResultHoldTime_100000_1000000_Updater.incrementAndGet(this);
        } else {
            executeAndResultHoldTime_1000000_more_Updater.incrementAndGet(this);
        }
    }

    private void histogramRecord(long nanoSpan) {
        long millis = nanoSpan / 1000 / 1000;

        if (millis < 1) {
            histogram_0_1_Updater.incrementAndGet(this);
        } else if (millis < 10) {
            histogram_1_10_Updater.incrementAndGet(this);
        } else if (millis < 100) {
            histogram_10_100_Updater.incrementAndGet(this);
        } else if (millis < 1000) {
            histogram_100_1000_Updater.incrementAndGet(this);
        } else if (millis < 10000) {
            histogram_1000_10000_Updater.incrementAndGet(this);
        } else if (millis < 100000) {
            histogram_10000_100000_Updater.incrementAndGet(this);
        } else if (millis < 1000000) {
            histogram_100000_1000000_Updater.incrementAndGet(this);
        } else {
            histogram_1000000_more_Updater.incrementAndGet(this);
        }
    }

    public void addExecuteTime(long nanoSpan) {
        executeSpanNanoTotalUpdater.addAndGet(this, nanoSpan);

        for (; ; ) {
            long current = executeSpanNanoMaxUpdater.get(this);
            if (current >= nanoSpan) {
                break;
            }

            if (executeSpanNanoMaxUpdater.compareAndSet(this, current, nanoSpan)) {
                // 可能不准确，但是绝大多数情况下都会正确，性能换取一致性
                executeNanoSpanMaxOccurTime = System.currentTimeMillis();
                break;
            }
        }

        histogramRecord(nanoSpan);
    }

    public long getExecuteMillisTotal() {
        return executeSpanNanoTotal / (1000 * 1000);
    }

    public long getExecuteMillisMax() {
        return executeSpanNanoMax / (1000 * 1000);
    }

    public long getErrorCount() {
        return executeErrorCount;
    }

    @Override
    public long getExecuteBatchSizeMax() {
        return executeBatchSizeMax;
    }

    public long getInTransactionCount() {
        return inTransactionCount;
    }

    public void incrementInTransactionCount() {
        inTransactionCountUpdater.incrementAndGet(this);
    }

    private static CompositeType COMPOSITE_TYPE;

    public static CompositeType getCompositeType() throws JMException {
        if (COMPOSITE_TYPE != null) {
            return COMPOSITE_TYPE;
        }

        OpenType<?>[] indexTypes = new OpenType<?>[]{
                // 0 - 4
                SimpleType.LONG, //
                SimpleType.STRING, //
                SimpleType.STRING, //
                SimpleType.LONG, //
                SimpleType.LONG, //

                // 5 - 9
                SimpleType.LONG, //
                SimpleType.DATE, //
                SimpleType.LONG, //
                JMXUtils.getThrowableCompositeType(), //
                SimpleType.LONG, //
                //

                // 10 - 14
                SimpleType.LONG, //
                SimpleType.DATE, //
                SimpleType.LONG, //
                SimpleType.LONG, //
                SimpleType.LONG, //
                //

                // 15 - 19
                SimpleType.LONG, //
                SimpleType.STRING, //
                SimpleType.STRING, //
                SimpleType.STRING, //
                SimpleType.STRING, //
                //

                // 20 - 24
                SimpleType.STRING, //
                SimpleType.DATE, //
                SimpleType.STRING, //
                SimpleType.LONG, //
                SimpleType.STRING, //

                // 25 - 29
                new ArrayType<Long>(SimpleType.LONG, true), //
                SimpleType.STRING, //
                SimpleType.LONG, //
                SimpleType.LONG, //
                new ArrayType<Long>(SimpleType.LONG, true), //

                // 30 - 34
                new ArrayType<Long>(SimpleType.LONG, true), //
                new ArrayType<Long>(SimpleType.LONG, true), //
                SimpleType.LONG, //
                SimpleType.LONG, //
                SimpleType.LONG, //

                // 35 - 39
                SimpleType.LONG, //
                SimpleType.LONG, //
                SimpleType.LONG, //
                SimpleType.LONG, //
                SimpleType.LONG, //

                // 40 -
                SimpleType.LONG, //

        };

        String[] indexNames = {
                // 0 - 4
                "ID", //
                "DataSource", //
                "SQL", //
                "ExecuteCount", //
                "ErrorCount", //

                // 5 - 9
                "TotalTime", //
                "LastTime", //
                "MaxTimespan", //
                "LastError", //
                "EffectedRowCount", //

                // 10 - 14
                "FetchRowCount", //
                "MaxTimespanOccurTime", //
                "BatchSizeMax", //
                "BatchSizeTotal", //
                "ConcurrentMax", //

                // 15 - 19
                "RunningCount", //
                "Name", //
                "File", //
                "LastErrorMessage", //
                "LastErrorClass", //

                // 20 - 24
                "LastErrorStackTrace", //
                "LastErrorTime", //
                "DbType", //
                "InTransactionCount", //
                "URL", //

                // 25 - 29
                "Histogram", //
                "LastSlowParameters", //
                "ResultSetHoldTime", //
                "ExecuteAndResultSetHoldTime", //
                "FetchRowCountHistogram", //

                // 30 - 34
                "EffectedRowCountHistogram", //
                "ExecuteAndResultHoldTimeHistogram", //
                "EffectedRowCountMax", //
                "FetchRowCountMax", //
                "ClobOpenCount",

                // 35 -
                "BlobOpenCount", //
                "ReadStringLength", //
                "ReadBytesLength", //
                "InputStreamOpenCount", //
                "ReaderOpenCount", //

                // 40
                "HASH", //

                //
        };
        String[] indexDescriptions = indexNames;
        COMPOSITE_TYPE = new CompositeType("SqlStatistic", "Sql Statistic", indexNames, indexDescriptions, indexTypes);

        return COMPOSITE_TYPE;
    }

    public long getExecuteCount() {
        return getErrorCount() + getExecuteSuccessCount();
    }

    public Map<String, Object> getData() throws JMException {
        return getValue(false).getData();
    }

    public long[] getHistogramValues() {
        return new long[]{
                //
                histogram_0_1, //
                histogram_1_10, //
                histogram_10_100, //
                histogram_100_1000, //
                histogram_1000_10000, //
                histogram_10000_100000, //
                histogram_100000_1000000, //
                histogram_1000000_more //
        };
    }

    public long getHistogramSum() {
        long[] values = this.getHistogramValues();
        long sum = 0;
        for (int i = 0; i < values.length; ++i) {
            sum += values[i];
        }
        return sum;
    }

    public CompositeDataSupport getCompositeData() throws JMException {
        return new CompositeDataSupport(getCompositeType(), getData());
    }

    public Throwable getExecuteErrorLast() {
        return executeErrorLast;
    }

    public void error(Throwable error) {
        executeErrorCountUpdater.incrementAndGet(this);
        executeErrorLastTime = System.currentTimeMillis();
        executeErrorLast = error;

    }

    public long getResultSetHoldTimeMilis() {
        return getResultSetHoldTimeNano() / (1000 * 1000);
    }

    public long getExecuteAndResultSetHoldTimeMilis() {
        return getExecuteAndResultSetHoldTimeNano() / (1000 * 1000);
    }

    public long[] getFetchRowCountHistogramValues() {
        return new long[]{
                //
                fetchRowCount_0_1, //
                fetchRowCount_1_10, //
                fetchRowCount_10_100, //
                fetchRowCount_100_1000, //
                fetchRowCount_1000_10000, //
                fetchRowCount_10000_more //
        };
    }

    public long[] getUpdateCountHistogramValues() {
        return new long[]{
                //
                updateCount_0_1, //
                updateCount_1_10, //
                updateCount_10_100, //
                updateCount_100_1000, //
                updateCount_1000_10000, //
                updateCount_10000_more //
        };
    }

    public long[] getExecuteAndResultHoldTimeHistogramValues() {
        return new long[]{
                //
                executeAndResultHoldTime_0_1, //
                executeAndResultHoldTime_1_10, //
                executeAndResultHoldTime_10_100, //
                executeAndResultHoldTime_100_1000, //
                executeAndResultHoldTime_1000_10000, //
                executeAndResultHoldTime_10000_100000, //
                executeAndResultHoldTime_100000_1000000, //
                executeAndResultHoldTime_1000000_more //
        };
    }

    public long getExecuteAndResultHoldTimeHistogramSum() {
        long[] values = this.getExecuteAndResultHoldTimeHistogramValues();
        long sum = 0;
        for (int i = 0; i < values.length; ++i) {
            sum += values[i];
        }
        return sum;
    }

    public long getResultSetHoldTimeNano() {
        return resultSetHoldTimeNano;
    }

    public long getExecuteAndResultSetHoldTimeNano() {
        return executeAndResultSetHoldTime;
    }

    public void addResultSetHoldTimeNano(long nano) {
        resultSetHoldTimeNanoUpdater.addAndGet(this, nano);
    }

    public void addResultSetHoldTimeNano(long statementExecuteNano, long resultHoldTimeNano) {
        resultSetHoldTimeNanoUpdater.addAndGet(this, resultHoldTimeNano);
        executeAndResultSetHoldTimeUpdater.addAndGet(this, statementExecuteNano + resultHoldTimeNano);
        executeAndResultHoldTimeHistogramRecord(statementExecuteNano + resultHoldTimeNano);
        updateCount_0_1_Updater.incrementAndGet(this);
    }

    public boolean isRemoved() {
        return removed;
    }

    public void setRemoved(boolean removed) {
        this.removed = removed;
    }

    @Override
    public int compareTo(JdbcSqlStat o) {
        if (o.sqlHash == this.sqlHash) {
            return 0;
        }

        return this.id < o.id ? -1 : 1;
    }
}
