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

org.jooq.debug.impl.DebugProcessor Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2009-2013, Lukas Eder, [email protected]
 *                          Christopher Deckers, [email protected]
 * All rights reserved.
 *
 * This software is licensed to you under the Apache License, Version 2.0
 * (the "License"); You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * . Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * . Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * . Neither the name "jOOQ" nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package org.jooq.debug.impl;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.jooq.ExecuteContext;
import org.jooq.ExecuteType;
import org.jooq.debug.Breakpoint;
import org.jooq.debug.BreakpointHit;
import org.jooq.debug.Debugger;
import org.jooq.debug.ExecutionType;
import org.jooq.debug.LoggingListener;
import org.jooq.debug.QueryInfo;
import org.jooq.debug.QueryLog;
import org.jooq.debug.QueryMatcher;
import org.jooq.debug.QueryProcessor;
import org.jooq.debug.QueryType;
import org.jooq.debug.ResultLog;
import org.jooq.debug.impl.LocalDebugger.DebuggerRegistry;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;

/**
 * @author Christopher Deckers
 */
class DebugProcessor {

    private boolean    hasDebuggers;

    private long       startPreparationTime;
    private long       aggregatedPreparationDuration;

    private long       startBindTime;
    private long       endBindTime;

    private long       startExecutionTime;
    private long       endExecutionTime;
    private String     matchingSQL;
    private String     matchingParameterDescription;
    private String     effectiveSQL;
    private Breakpoint matchingBreakpoint;
    private Debugger   matchingDebugger = null;

    public void renderStart(ExecuteContext ctx) {
        hasDebuggers = !DebuggerRegistry.get().isEmpty();
        startPreparationTime = 0;
        aggregatedPreparationDuration = 0;
        startBindTime = 0;
        endBindTime = 0;
        startExecutionTime = 0;
        endExecutionTime = 0;
    }

    public void prepareStart(ExecuteContext ctx) {
        if(!hasDebuggers) {
            return;
        }
        startPreparationTime = System.currentTimeMillis();
    }

    public void prepareEnd(ExecuteContext ctx) {
        if(!hasDebuggers) {
            return;
        }
        aggregatedPreparationDuration += System.currentTimeMillis() - startPreparationTime;
        PreparedStatement statement = ctx.statement();
        if (ctx.type() == ExecuteType.ROUTINE) {
            ctx.statement(new TrackingCallableStatement((CallableStatement)statement));
        } else {
            ctx.statement(new TrackingPreparedStatement(statement));
        }
    }

    public void bindStart(ExecuteContext ctx) {
        if(!hasDebuggers) {
            return;
        }
        startBindTime = System.currentTimeMillis();
    }

    public void bindEnd(ExecuteContext ctx) {
        if(!hasDebuggers) {
            return;
        }
        endBindTime = System.currentTimeMillis();
    }

    public void executeStart(ExecuteContext ctx) {
        List debuggerList = DebuggerRegistry.get();
        boolean hasBreakpointHitHandler = false;
        if(!debuggerList.isEmpty()) {
            QueryInfo queryInfo = null;
            bp: for(Debugger debugger: debuggerList) {
                Breakpoint[] breakpoints = debugger.getBreakpoints();
                if(breakpoints != null) {
                    for(Breakpoint breakpoint: breakpoints) {
                        String sql_ = null;
                        String parameterDescription = null;
                        if(queryInfo == null) {
                            String[] sql = ctx.batchSQL();
                            QueryType queryType = QueryType.detectType(sql[0]);
                            if(sql.length == 1) {
                                sql_ = sql[0];
                                PreparedStatement statement = ctx.statement();
                                if(statement instanceof TrackingPreparedStatement) {
                                    parameterDescription = ((TrackingPreparedStatement) statement).getParameterDescription();
                                }
                            } else {
                                StringBuilder sb = new StringBuilder();
                                for(int i=0; i 0) {
                                        sb.append('\n');
                                    }
                                    sb.append(sql[i]);
                                }
                                sql_ = sb.toString();
                            }
                            queryInfo = new QueryInfo(queryType, sql, parameterDescription);
                        }
                        if(breakpoint.matches(queryInfo)) {
                            matchingSQL = sql_;
                            matchingParameterDescription = parameterDescription;
                            matchingDebugger = debugger;
                            matchingBreakpoint = breakpoint;
                            if(breakpoint.isBreaking()) {
                                hasBreakpointHitHandler = debugger.getBreakpointHitHandler() != null;
                            }
                            break bp;
                        }
                    }
                }
            }
        }
        // We consider raw SQL (not the parameters). If we want to match on parameters, this should be a separate matcher.
        // For batched execution of in-lined statements, we aggregate the statements as a multiple-line one for matching purposes.
        if(matchingBreakpoint != null) {
            QueryProcessor beforeExecutionProcessor = matchingBreakpoint.getBeforeExecutionProcessor();
            if(beforeExecutionProcessor != null) {
                String sql = beforeExecutionProcessor.processSQL(matchingSQL);
                long subStartExecutionTime = System.currentTimeMillis();
                executeSQL(ctx, sql);
                long subEndExecutionTime = System.currentTimeMillis();

                // Log result of pre-processing.
                for (Debugger debugger : debuggerList) {
                    LoggingListener listener = debugger.getLoggingListener();

                    if (listener != null) {
                        QueryType type = QueryType.detectType(sql);
                        QueryInfo info = new QueryInfo(type, new String[] { sql }, null);
                        QueryLog log = new QueryLog(info, null, null, subEndExecutionTime - subStartExecutionTime);
                        QueryMatcher[] matchers = listener.getMatchers();

                        if (matchers == null) {
                            listener.logQuery(log);
                        }
                        else {
                            for (QueryMatcher matcher : matchers) {
                                if (matcher.matches(log.getQueryInfo())) {
                                    listener.logQuery(log);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            String mainSQL = null;
            QueryProcessor replacementExecutionProcessor = matchingBreakpoint.getReplacementExecutionProcessor();
            if(replacementExecutionProcessor != null) {
                mainSQL = replacementExecutionProcessor.processSQL(matchingSQL);
                matchingParameterDescription = null;
                try {
                    ctx.statement().close();
                    ctx.sql(mainSQL);
                    ctx.statement(ctx.connection().prepareStatement(mainSQL));
                } catch(Exception e) {
                    // TODO: how to process properly breakpoint errors??
                    throw new RuntimeException(e);
                }
            }
            ExecutionType executionType = ExecutionType.RUN;
            if(hasBreakpointHitHandler) {
                effectiveSQL = mainSQL != null? mainSQL: matchingSQL;
                // TODO: find a way for the handler to replace the statement (not just step over).
                Thread currentThread = Thread.currentThread();
                long threadID = currentThread.getId();
                String threadName = currentThread.getName();
                StackTraceElement[] callerStackTraceElements = currentThread.getStackTrace();
                callerStackTraceElements = Arrays.copyOfRange(callerStackTraceElements, 2, callerStackTraceElements.length);
                BreakpointHit breakpointHit = new BreakpointHit(matchingBreakpoint.getID(), effectiveSQL, matchingParameterDescription, threadID, threadName, callerStackTraceElements, true);
                matchingDebugger.processBreakpointBeforeExecutionHit(ctx, breakpointHit);
                // Breakpoint has an answer.
                if(breakpointHit.getBreakpointID() == null) {
                    executionType = breakpointHit.getExecutionType();
                    String sql = breakpointHit.getSQL();
                    if(sql != null) {
                        effectiveSQL = sql;
                        matchingParameterDescription = null;
                        try {
                            ctx.statement().close();
                            ctx.sql(effectiveSQL);
                            ctx.statement(ctx.connection().prepareStatement(effectiveSQL));
                        } catch(Exception e) {
                            // TODO: how to process properly breakpoint errors??
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
            if(executionType != ExecutionType.STEP) {
                matchingDebugger = null;
            }
            switch(executionType) {
                case FAIL: {
                    throw new DataAccessException("Failing SQL statement.");
                }
                case SKIP: {
                    try {
                        ctx.statement().close();
                        // Better return possibility? Based on originating query?
                        String sql = DSL.using(ctx.configuration().dialect()).selectZero().where("1 = 2").getSQL();
                        ctx.sql(sql);
                        ctx.statement(ctx.connection().prepareStatement(sql));
                    } catch(Exception e) {
                        // TODO: how to process properly breakpoint errors??
                        throw new RuntimeException(e);
                    }
                    break;
                }
            }
        }
        if(!hasDebuggers) {
            return;
        }
        startExecutionTime = System.currentTimeMillis();
    }

    private void executeSQL(ExecuteContext ctx, String sql) {
        Statement statement = null;
        try {
            statement = ctx.connection().createStatement();
            statement.execute(sql);
        } catch(Exception e) {
            // TODO: how to process properly breakpoint errors??
            throw new RuntimeException(e);
        } finally {
            if(statement != null) {
                try {
                    statement.close();
                } catch(Exception e) {
                    // No error for closing problems.
                    e.printStackTrace();
                }
            }
        }
    }

    public void executeEnd(ExecuteContext ctx) {
        if(!hasDebuggers) {
            return;
        }
        endExecutionTime = System.currentTimeMillis();
        List debuggers = DebuggerRegistry.get();
        if(!debuggers.isEmpty()) {
            boolean hasListener = false;
            for (Debugger debugger : debuggers) {
                LoggingListener listener = debugger.getLoggingListener();
                if (listener != null) {
                    hasListener = true;
                    break;
                }
            }

            if(hasListener) {
                String[] sql = ctx.batchSQL();
                QueryType type = QueryType.detectType(sql[0]);
                String parameterDescription = null;
                if (sql.length == 1) {
                    PreparedStatement statement = ctx.statement();
                    if (statement instanceof TrackingPreparedStatement) {
                        parameterDescription = ((TrackingPreparedStatement) statement).getParameterDescription();
                    }
                }
                QueryInfo info = new QueryInfo(type, sql, parameterDescription);
                final QueryLog log = new QueryLog(info, startPreparationTime == 0? null: aggregatedPreparationDuration, startBindTime == 0? null: endBindTime - startBindTime, endExecutionTime - startExecutionTime);
                final List listeners = new ArrayList(debuggers.size());
                for (Debugger debugger : debuggers) {
                    LoggingListener listener = debugger.getLoggingListener();

                    if (listener != null) {
                        QueryMatcher[] matchers = listener.getMatchers();
                        if (matchers == null) {
                            listeners.add(listener);
                            listener.logQuery(log);
                        }
                        else {
                            for (QueryMatcher matcher : matchers) {
                                if (matcher.matches(log.getQueryInfo())) {
                                    listeners.add(listener);
                                    listener.logQuery(log);
                                    break;
                                }
                            }
                        }
                    }
                }
                ResultSet resultSet = ctx.resultSet();
                if (resultSet != null && !listeners.isEmpty()) {
                    ResultSet newResultSet = new TrackingResultSet(resultSet) {
                        @Override
                        protected void notifyData(long lifeTime, int readRows, int readCount, int writeCount) {
                            ResultLog resultLog = null;
                            for (LoggingListener loggingListener : listeners) {
                                if (resultLog == null) {
                                    resultLog = new ResultLog(log.getID(), lifeTime, readRows, readCount, writeCount);
                                }
                                loggingListener.logResult(resultLog);
                            }
                        }
                    };
                    ctx.resultSet(newResultSet);
                }
            }
        }
        if(matchingDebugger != null) {
            Thread currentThread = Thread.currentThread();
            long threadID = currentThread.getId();
            String threadName = currentThread.getName();
            StackTraceElement[] callerStackTraceElements = currentThread.getStackTrace();
            callerStackTraceElements = Arrays.copyOfRange(callerStackTraceElements, 2, callerStackTraceElements.length);
            matchingDebugger.processBreakpointAfterExecutionHit(ctx, new BreakpointHit(matchingBreakpoint.getID(), effectiveSQL, matchingParameterDescription, threadID, threadName, callerStackTraceElements, false));
        }
        if(matchingBreakpoint != null) {
            QueryProcessor afterExecutionProcessor = matchingBreakpoint.getAfterExecutionProcessor();
            matchingBreakpoint = null;
            if(afterExecutionProcessor != null) {
                String sql = afterExecutionProcessor.processSQL(matchingSQL);
                long subStartExecutionTime = System.currentTimeMillis();
                executeSQL(ctx, sql);
                long subEndExecutionTime = System.currentTimeMillis();
                // Log result of pre-processing.
                for (Debugger debugger : debuggers) {
                    LoggingListener listener = debugger.getLoggingListener();

                    if (listener != null) {
                        QueryType type = QueryType.detectType(sql);
                        QueryInfo info = new QueryInfo(type, new String[] {sql}, null);
                        QueryLog log = new QueryLog(info, null, null, subEndExecutionTime - subStartExecutionTime);
                        QueryMatcher[] matchers = listener.getMatchers();

                        if (matchers == null) {
                            listener.logQuery(log);
                        }
                        else {
                            for (QueryMatcher matcher : matchers) {
                                if (matcher.matches(log.getQueryInfo())) {
                                    listener.logQuery(log);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

//    private long startFetchTime;
//    private long endFetchTime;
//
//    @Override
//    public void fetchStart(ExecuteContext ctx) {
//        if(!isLogging) {
//            return;
//        }
//        startFetchTime = System.currentTimeMillis();
//    }
//
//    @Override
//    public void fetchEnd(ExecuteContext ctx) {
//        if(!isLogging) {
//            return;
//        }
//        endFetchTime = System.currentTimeMillis();
//    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy