diff options
Diffstat (limited to 'activerecord/lib/active_record/explain.rb')
-rw-r--r-- | activerecord/lib/active_record/explain.rb | 38 |
1 files changed, 29 insertions, 9 deletions
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb index 4d013f0ef4..abe6fff5d5 100644 --- a/activerecord/lib/active_record/explain.rb +++ b/activerecord/lib/active_record/explain.rb @@ -1,5 +1,5 @@ module ActiveRecord - module Explain # :nodoc: + module Explain # logging_query_plan calls could appear nested in the call stack. In # particular this happens when a relation fetches its records, since # that results in find_by_sql calls downwards. @@ -13,33 +13,34 @@ module ActiveRecord # whole. That is, the threshold is not checked against each individual # query, but against the duration of the entire block. This approach is # convenient for relations. - def logging_query_plan(&block) - if (t = auto_explain_threshold_in_seconds) && !Thread.current[LOGGING_QUERY_PLAN] + def logging_query_plan(&block) # :nodoc: + threshold = auto_explain_threshold_in_seconds + if threshold && !Thread.current[LOGGING_QUERY_PLAN] && !Thread.current[SILENCED] begin Thread.current[LOGGING_QUERY_PLAN] = true start = Time.now result, sqls, binds = collecting_sqls_for_explain(&block) - logger.warn(exec_explain(sqls, binds)) if Time.now - start > t + logger.warn(exec_explain(sqls, binds)) if Time.now - start > threshold result ensure Thread.current[LOGGING_QUERY_PLAN] = false end else - block.call + yield end end # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on # our own EXPLAINs now matter how loopingly beautiful that would be. SKIP_EXPLAIN_FOR = %w(SCHEMA EXPLAIN) - def ignore_explain_notification?(payload) + def ignore_explain_notification?(payload) # :nodoc: payload[:exception] || SKIP_EXPLAIN_FOR.include?(payload[:name]) end # Collects all queries executed while the passed block runs. Returns an # array with three elements, the result of the block, the strings with the # queries, and their respective bindings. - def collecting_sqls_for_explain(&block) + def collecting_sqls_for_explain # :nodoc: sqls = [] binds = [] callback = lambda do |*args| @@ -52,7 +53,7 @@ module ActiveRecord result = nil ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do - result = block.call + result = yield end [result, sqls, binds] @@ -60,7 +61,7 @@ module ActiveRecord # Makes the adapter execute EXPLAIN for the given queries and bindings. # Returns a formatted string ready to be logged. - def exec_explain(sqls, binds) + def exec_explain(sqls, binds) # :nodoc: sqls.zip(binds).map do |sql, bind| [].tap do |msg| msg << "EXPLAIN for: #{sql}" @@ -72,5 +73,24 @@ module ActiveRecord end.join("\n") end.join("\n") end + + SILENCED = :silence_explain + + # Silences automatic EXPLAIN logging for the duration of the block. + # + # This has high priority, no EXPLAINs will be run even if downwards + # the threshold is set to 0. + # + # As the name of the method suggests this only applies to automatic + # EXPLAINs, manual calls to +ActiveRecord::Relation#explain+ run. + def silence_auto_explain + # Implemented as a flag rather that setting the threshold to nil + # because we should not depend on a value that may be changed + # downwards. + Thread.current[SILENCED] = true + yield + ensure + Thread.current[SILENCED] = false + end end end |