All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lealone.sql.executor.YieldableBase Maven / Gradle / Ivy

/*
 * Copyright Lealone Database Group.
 * Licensed under the Server Side Public License, v 1.
 * Initial Developer: zhh
 */
package org.lealone.sql.executor;

import java.sql.SQLException;
import java.util.ArrayList;

import org.lealone.common.exceptions.DbException;
import org.lealone.common.trace.Trace;
import org.lealone.common.trace.TraceModuleType;
import org.lealone.db.Constants;
import org.lealone.db.Database;
import org.lealone.db.api.DatabaseEventListener;
import org.lealone.db.api.ErrorCode;
import org.lealone.db.async.AsyncHandler;
import org.lealone.db.async.AsyncResult;
import org.lealone.db.lock.DbObjectLock;
import org.lealone.db.session.ServerSession;
import org.lealone.db.session.SessionStatus;
import org.lealone.db.value.Value;
import org.lealone.sql.PreparedSQLStatement;
import org.lealone.sql.PreparedSQLStatement.Yieldable;
import org.lealone.sql.SQLStatementExecutor;
import org.lealone.sql.StatementBase;
import org.lealone.sql.expression.Parameter;

public abstract class YieldableBase implements Yieldable {

    protected StatementBase statement;
    protected final ServerSession session;
    protected final Trace trace;
    protected final AsyncHandler> asyncHandler;
    protected AsyncResult asyncResult;
    protected long startTimeNanos;
    protected boolean started;

    protected volatile Throwable pendingException;
    protected volatile boolean stopped;
    protected volatile boolean yieldEnabled = true;

    public YieldableBase(StatementBase statement, AsyncHandler> asyncHandler) {
        this.statement = statement;
        this.session = statement.getSession();
        this.trace = session.getTrace(TraceModuleType.COMMAND);
        this.asyncHandler = asyncHandler;
    }

    // 子类通常只需要实现以下三个方法
    protected boolean startInternal() {
        return false;
    }

    protected void stopInternal() {
    }

    protected abstract void executeInternal();

    protected void setPendingException(Throwable pendingException) {
        if (this.pendingException == null)
            this.pendingException = pendingException;
    }

    protected void setResult(T result, int rowCount) {
        if (result != null) {
            asyncResult = new AsyncResult<>(result);
        }
        if (rowCount < 0) {
            setProgress(DatabaseEventListener.STATE_STATEMENT_WAITING);
        } else {
            statement.trace(startTimeNanos, rowCount);
            setProgress(DatabaseEventListener.STATE_STATEMENT_END);
        }
    }

    @Override
    public T getResult() {
        return asyncResult != null ? asyncResult.getResult() : null;
    }

    @Override
    public int getPriority() {
        return statement.getPriority();
    }

    @Override
    public ServerSession getSession() {
        return session;
    }

    @Override
    public PreparedSQLStatement getStatement() {
        return statement;
    }

    @Override
    public void setExecutor(SQLStatementExecutor executor) {
        statement.setExecutor(executor);
    }

    @Override
    public final void run() {
        try {
            if (!started) {
                if (start()) {
                    session.setStatus(SessionStatus.STATEMENT_RUNNING);
                    return;
                }
                started = true;
            }

            session.getDatabase().checkPowerOff();
            executeInternal();
        } catch (Throwable t) {
            pendingException = t;
        }

        if (pendingException != null) {
            if (DbObjectLock.LOCKED_EXCEPTION == pendingException) // 忽略
                pendingException = null;
            else
                handleException(pendingException);
        } else if (session.getStatus() == SessionStatus.STATEMENT_COMPLETED) {
            stop();
        }
    }

    private boolean start() {
        if (session.isExclusiveMode() && session.getDatabase().addWaitingSession(session)) {
            return true;
        }
        if (session.getDatabase().getQueryStatistics() || trace.isInfoEnabled()) {
            startTimeNanos = System.nanoTime();
        }
        recompileIfNeeded();
        session.startCurrentCommand(statement);
        setProgress(DatabaseEventListener.STATE_STATEMENT_START);
        statement.checkParameters();
        return startInternal();
    }

    private void recompileIfNeeded() {
        if (statement.needRecompile()) {
            statement.setModificationMetaId(0);
            String sql = statement.getSQL();
            ArrayList oldParams = statement.getParameters();
            statement = (StatementBase) session.parseStatement(sql);
            long mod = statement.getModificationMetaId();
            statement.setModificationMetaId(0);
            ArrayList newParams = statement.getParameters();
            for (int i = 0, size = newParams.size(); i < size; i++) {
                Parameter old = oldParams.get(i);
                if (old.isValueSet()) {
                    Value v = old.getValue(session);
                    Parameter p = newParams.get(i);
                    p.setValue(v);
                }
            }
            statement.prepare();
            statement.setModificationMetaId(mod);
        }
    }

    private void setProgress(int state) {
        session.getDatabase().setProgress(state, statement.getSQL(), 0, 0);
    }

    private void handleException(Throwable t) {
        DbException e = DbException.convert(t).addSQL(statement.getSQL());
        SQLException s = e.getSQLException();
        Database database = session.getDatabase();
        database.exceptionThrown(s, statement.getSQL());
        if (s.getErrorCode() == ErrorCode.OUT_OF_MEMORY) {
            database.shutdownImmediately();
            throw e;
        }
        database.checkPowerOff();
        if (!statement.isQuery()) {
            if (s.getErrorCode() == ErrorCode.DEADLOCK_1) {
                session.rollback();
            } else {
                // 手动提交模式如果不设置状态会导致不能执行下一条语句
                session.setStatus(SessionStatus.STATEMENT_COMPLETED);
                session.rollbackCurrentCommand();
            }
        }
        if (asyncHandler != null) {
            asyncResult = new AsyncResult<>(e);
            asyncHandler.handle(asyncResult);
            asyncResult = null; // 不需要再回调了
            stop();
        } else {
            stop();
            throw e;
        }
    }

    @Override
    public void stop() {
        stopped = true;
        stopInternal();
        session.stopCurrentCommand(statement, asyncHandler, asyncResult);

        if (startTimeNanos > 0 && trace.isInfoEnabled()) {
            long timeMillis = (System.nanoTime() - startTimeNanos) / 1000 / 1000;
            // 如果一条sql的执行时间大于100毫秒,记下它
            if (timeMillis > Constants.SLOW_QUERY_LIMIT_MS) {
                trace.info("slow query: {0} ms, sql: {1}", timeMillis, statement.getSQL());
            }
        }
    }

    @Override
    public boolean isStopped() {
        return stopped;
    }

    public void disableYield() {
        yieldEnabled = false;
    }

    public boolean yieldIfNeeded(int rowNumber) {
        if (statement.setCurrentRowNumber(rowNumber, yieldEnabled)) {
            session.setStatus(SessionStatus.STATEMENT_YIELDED);
            return true;
        }
        return false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy