<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>lib/query_reviewer/views/_profile.rhtml</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -3,22 +3,43 @@ QueryReviewer
 
 QueryReviewer is an advanced SQL query analyzer.  It accomplishes the following goals:
 
- * View all EXPLAIN output for all SELECT queries to generate a page
+ * View all EXPLAIN output for all SELECT queries to generate a page (and optionally SHOW PROFILE ALL)
  * Rate a page's SQL usage into one of three categories: OK, WARNING, CRITICAL
- * Attach meaningful warnings to individual queries
+ * Attach meaningful warnings to individual queries, and collections of queries
  * Display interactive summary on page
 
 All you have to do is install it.  You can optionally run:
   rake query_reviewer:setup
 
-Which will create config/query_reviewer.yml.  This file provides a few useful settings:
- * enabled: whether any output or query analysis is performed.  Set this FALSE in production!
+Which will create config/query_reviewer.yml, see below for what these options mean.  If you don't have a config file,
+the plugin will use the default in vendor/plugins/query_reviewer.
+
+
+Configuration
+=============
+
+The configuration file allows you to set configuration parameters shared across all rails environment, as well as
+overriding those shared parameteres with environment-specific parameters (such as disabling analysis on production!)
+
+ * enabled: whether any output or query analysis is performed.  Set this false in production!
  * inject_view: controls whether the output automatically is injected before the &lt;/body&gt; in HTML output.
+ * profiling: when enabled, runs the MySQL SET PROFILING=1 for queries longer than the warn_duration_threshold / 2.0
+ * production_data: whether the duration of a query should be taken into account (if you don't have real data, don't let query duration effect you!)
+
+ * stack_trace_lines: number of lines of call stack to include in the &quot;short&quot; version of the stack trace
+ * trace_includes_vendor: whether the &quot;short&quot; verison of the stack trace should include files in /vendor
+ * trace_includes_lib: whether the &quot;short&quot; verison of the stack trace should include files in /lib
+
  * warn_severity: the severity of problem that merits &quot;WARNING&quot; status
  * critical_severity: the severity of problem that merits &quot;CRITICAL&quot; status
+
  * warn_query_count: the number of queries in a single request that merits &quot;WARNING&quot; status
  * critical_query_count: the number of queries in a single request that merits &quot;CRITICAL&quot; status
- 
+
+ * warn_duration_threshold: how long a query must take in seconds (float) before it's considered &quot;WARNING&quot;
+ * critical_duration_threshold: how long a query must take in seconds (float) before it's considered &quot;CRITICIAL&quot;
+
+
 Example
 =======
 
@@ -27,4 +48,4 @@ If you disable the inject_view option, you'll need to manually put the analyzer'
 &lt;%= query_review_output %&gt;
 
 
-Copyright (c) 2007 Kongregate, released under the MIT license
+Copyright (c) 2007-2008 Kongregate &amp; David Stevenson, released under the MIT license</diff>
      <filename>README</filename>
    </modified>
    <modified>
      <diff>@@ -5,13 +5,29 @@ module QueryReviewer
     end
 
     def select_with_review(sql, name = nil)
+      @logger.silence { execute(&quot;SET PROFILING=1&quot;) } if QueryReviewer::CONFIGURATION[&quot;profiling&quot;]
+      t1 = Time.now
       query_results = select_without_review(sql, name)
+      t2 = Time.now
 
       if @logger and sql =~ /^select/i
+        use_profiling = QueryReviewer::CONFIGURATION[&quot;profiling&quot;]
+        use_profiling &amp;&amp;= (t2 - t1) &gt;= QueryReviewer::CONFIGURATION[&quot;warn_duration_threshold&quot;].to_f / 2.0 if QueryReviewer::CONFIGURATION[&quot;production_data&quot;]
+        
+        if use_profiling
+          @logger.silence { execute(&quot;SET PROFILING=1&quot;) }
+          select_without_review(sql, name)
+          profile = @logger.silence { select_without_review(&quot;SHOW PROFILE ALL&quot;, name) }
+          @logger.silence { execute(&quot;SET PROFILING=0&quot;) }
+        else
+          profile = nil
+        end
+
         cols = @logger.silence do
           select_without_review(&quot;explain #{sql}&quot;, name)
         end
-        query = SqlQuery.new(sql, cols)
+
+        query = SqlQuery.new(sql, cols, t2 - t1, profile)
         Thread.current[&quot;queries&quot;] &lt;&lt; query if Thread.current[&quot;queries&quot;] &amp;&amp; Thread.current[&quot;queries&quot;].respond_to?(:&lt;&lt;)
         @logger.debug(format_log_entry(&quot;Analyzing #{name}\n&quot;, query.to_table)) if @logger.level &lt;= Logger::INFO
       end</diff>
      <filename>lib/query_reviewer/mysql_adapter_extensions.rb</filename>
    </modified>
    <modified>
      <diff>@@ -33,7 +33,7 @@ module QueryReviewer
     def analyze_key!
       if self.key == &quot;const&quot;
         praise &quot;Way to go!&quot;
-      elsif self.key.blank? &amp;&amp; !self.extra.include?(&quot;select tables optimized away&quot;)
+      elsif self.key.blank? &amp;&amp; !self.rows.blank?
         warn :severity =&gt; 6, :field =&gt; &quot;key&quot;, :desc =&gt; &quot;No index was used here. In this case, that meant scanning #{self.rows} rows.&quot;
       end
     end</diff>
      <filename>lib/query_reviewer/mysql_analyzer.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,16 +1,21 @@
+require &quot;ostruct&quot;
+
 module QueryReviewer
   # a single SQL SELECT query
   class SqlQuery
-    attr_reader :sql, :rows, :subqueries, :trace, :id
+    attr_reader :sql, :rows, :subqueries, :trace, :id, :profile, :duration
 
     cattr_accessor :next_id
     self.next_id = 1
 
-    def initialize(sql, rows)
+    def initialize(sql, rows, duration = 0.0, profile = nil)
       @rows = rows
       @sql = sql
       @subqueries = rows.collect{|row| SqlSubQuery.new(self, row)}
       @id = (self.class.next_id += 1)
+      @profile = profile.collect { |p| OpenStruct.new(p) } if profile
+      @duration = duration.to_f
+      @warnings = []
       get_trace
     end
 
@@ -19,7 +24,7 @@ module QueryReviewer
     end
 
     def warnings
-      self.subqueries.collect(&amp;:warnings).flatten
+      self.subqueries.collect(&amp;:warnings).flatten + @warnings
     end
 
     def has_warnings?
@@ -30,8 +35,19 @@ module QueryReviewer
       self.warnings.empty? ? 0 : self.warnings.collect(&amp;:severity).max
     end
 
+    def table
+      @subqueries.first.table
+    end
+
     def analyze!
       self.subqueries.collect(&amp;:analyze!)
+      if duration
+        if duration &gt;= QueryReviewer::CONFIGURATION[&quot;critical_duration_threshold&quot;]
+          warn(:problem =&gt; &quot;Query took #{duration} seconds&quot;, :severity =&gt; 9)
+        elsif duration &gt;= QueryReviewer::CONFIGURATION[&quot;warn_duration_threshold&quot;]
+          warn(:problem =&gt; &quot;Query took #{duration} seconds&quot;, :severity =&gt; QueryReviewer::CONFIGURATION[&quot;critical_severity&quot;])
+        end
+      end
     end
 
     def to_hash
@@ -52,5 +68,11 @@ module QueryReviewer
     def get_trace
       @trace = Kernel.caller
     end
+    
+    def warn(options)
+      options[:query] = self
+      options[:table] ||= self.table
+      @warnings &lt;&lt; QueryWarning.new(options)
+    end
   end
 end
\ No newline at end of file</diff>
      <filename>lib/query_reviewer/sql_query.rb</filename>
    </modified>
    <modified>
      <diff>@@ -20,6 +20,10 @@ module QueryReviewer
       self.send(method_name.to_sym)
     end
 
+    def table
+      @table[:table]
+    end
+
     private
 
     def warn(options)
@@ -29,7 +33,7 @@ module QueryReviewer
         options[:problem] = (&quot;#{field.to_s.titleize}: #{val.blank? ? &quot;(blank)&quot; : val}&quot;)
       end
       options[:query] = self
-      options[:table] = @table[:table]
+      options[:table] = self.table
       @warnings &lt;&lt; QueryWarning.new(options)
     end
 </diff>
      <filename>lib/query_reviewer/sql_sub_query.rb</filename>
    </modified>
    <modified>
      <diff>@@ -24,14 +24,25 @@
 		No queries to display.
 	&lt;% else %&gt;
 		&lt;ul class=&quot;small&quot;&gt;
-			&lt;% @queries.queries.reject{|q| q.has_warnings?}.each do |query| %&gt;
+			&lt;% @queries.queries.reject{|q| q.has_warnings?}.sort{|a,b| b.duration &lt;=&gt; a.duration}.each do |query| %&gt;
 			&lt;li&gt;
-				&lt;%= render :partial =&gt; &quot;/query_sql&quot;, :object =&gt; query %&gt;
+        &lt;% if QueryReviewer::CONFIGURATION[&quot;production_data&quot;] %&gt;
+          &lt;%= duration_with_color(query.duration.to_f) %&gt;s
+        &lt;% end %&gt;
+        &lt;%= render :partial =&gt; &quot;/query_sql&quot;, :object =&gt; query %&gt;
           &lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query.id %&gt;_explain')&quot; title=&quot;show/hide sql&quot;&gt;EXPLN&lt;/a&gt;
+          &lt;% if QueryReviewer::CONFIGURATION[&quot;profiling&quot;] &amp;&amp; query.profile %&gt;
+            &lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query.id %&gt;_profile')&quot; title=&quot;show/hide profile&quot;&gt;PROF&lt;/a&gt;
+          &lt;% end %&gt;
           &lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query.id %&gt;_trace')&quot; title=&quot;show/hide stack trace&quot;&gt;TRACE&lt;/a&gt;
           &lt;div style=&quot;display: none&quot; id=&quot;warning_&lt;%= query.id %&gt;_explain&quot; class=&quot;indent small tbpadded&quot;&gt;
             &lt;%= render :partial =&gt; &quot;/explain&quot;, :locals =&gt; {:query =&gt; query} %&gt;
           &lt;/div&gt;
+          &lt;% if QueryReviewer::CONFIGURATION[&quot;profiling&quot;] &amp;&amp; query.profile %&gt;
+            &lt;div style=&quot;display: none&quot; id=&quot;warning_&lt;%= query.id %&gt;_profile&quot; class=&quot;indent small&quot;&gt;
+              &lt;%= render :partial =&gt; &quot;/profile&quot;, :locals =&gt; {:query =&gt; query} %&gt;
+            &lt;/div&gt;
+          &lt;% end %&gt;
           &lt;div style=&quot;display: none&quot; id=&quot;warning_&lt;%= query.id %&gt;_trace&quot; class=&quot;indent small&quot;&gt;
             &lt;%= render :partial =&gt; &quot;/trace&quot;, :object =&gt; query.relevant_trace, :locals =&gt; {:query_id =&gt; query.id, :full_trace =&gt; query.full_trace} %&gt;
           &lt;/div&gt;</diff>
      <filename>lib/query_reviewer/views/_box_body.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -1,15 +1,23 @@
 &lt;li id=&quot;query_&lt;%= query_with_warning.id %&gt;&quot;&gt;
 	&lt;div&gt;
 		&lt;%= render :partial =&gt; &quot;/spectrum&quot;, :locals =&gt; {:severity =&gt; query_with_warning.max_severity} %&gt;
-		&lt;p&gt;
+    &lt;% if QueryReviewer::CONFIGURATION[&quot;production_data&quot;] %&gt;
+      &lt;div style=&quot;float: left; padding-right: 5px;&quot;&gt;
+        &lt;%= duration_with_color(query_with_warning.duration.to_f) %&gt;s
+      &lt;/div&gt;
+    &lt;% end %&gt;
+    &lt;p&gt;
 			&lt;i&gt;Table &lt;%= (query_with_warning.warnings.detect {|w| !w.table.blank? } || query_with_warning.warnings.last).table %&gt;:&lt;/i&gt;
 			&lt;% query_with_warning.warnings.sort{|a,b| a.severity &lt;=&gt; b.severity}.reverse.each_with_index do |warn, index| %&gt;
-				&lt;span style=&quot;color: &lt;%= severity_color warn.severity%&gt;&quot; title=&quot;&lt;%= warn.desc%&gt;&quot;&gt;&lt;%= warn.problem %&gt;&lt;/span&gt;&lt;%= &quot;, &quot; if index &lt; query_with_warning.warnings.length - 1 %&gt;
+				&lt;span style=&quot;color: &lt;%= severity_color warn.severity%&gt;;&quot; title=&quot;&lt;%= warn.desc%&gt;&quot;&gt;&lt;%= warn.problem %&gt;&lt;/span&gt;&lt;%= &quot;, &quot; if index &lt; query_with_warning.warnings.length - 1 %&gt;
 			&lt;% end %&gt;
 			&lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query_with_warning.id %&gt;_desc')&quot; title=&quot;show/hide warning message&quot;&gt;MSG&lt;/a&gt;
 			&lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query_with_warning.id %&gt;_sql')&quot; title=&quot;show/hide sql&quot;&gt;SQL&lt;/a&gt;
-			&lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query_with_warning.id %&gt;_explain')&quot; title=&quot;show/hide sql&quot;&gt;EXPLN&lt;/a&gt;
-			&lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query_with_warning.id %&gt;_trace')&quot; title=&quot;show/hide stack trace&quot;&gt;TRACE&lt;/a&gt;
+			&lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query_with_warning.id %&gt;_explain')&quot; title=&quot;show/hide explain output&quot;&gt;EXPLN&lt;/a&gt;
+      &lt;% if QueryReviewer::CONFIGURATION[&quot;profiling&quot;] &amp;&amp; query_with_warning.profile %&gt;
+  			&lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query_with_warning.id %&gt;_profile')&quot; title=&quot;show/hide profile output&quot;&gt;PROF&lt;/a&gt;
+      &lt;% end %&gt;
+      &lt;a href=&quot;javascript: query_review_toggle('warning_&lt;%= query_with_warning.id %&gt;_trace')&quot; title=&quot;show/hide stack trace&quot;&gt;TRACE&lt;/a&gt;
 			&lt;% if ignore_hash?(query_with_warning.to_hash) %&gt;
 				&lt;a href=&quot;javascript: remove_ignore_hash('&lt;%= query_with_warning.to_hash %&gt;'); query_review_hide('query_&lt;%= query_with_warning.id %&gt;')&quot; title=&quot;stop ignore this query from now on&quot;&gt;UNIGNR&lt;/a&gt;
 			&lt;% else %&gt;
@@ -28,7 +36,12 @@
 	&lt;div style=&quot;display: none&quot; id=&quot;warning_&lt;%= query_with_warning.id %&gt;_explain&quot; class=&quot;indent small tbpadded&quot;&gt;
 		&lt;%= render :partial =&gt; &quot;/explain&quot;, :locals =&gt; {:query =&gt; query_with_warning} %&gt;
 	&lt;/div&gt;
-	&lt;div style=&quot;display: none&quot; id=&quot;warning_&lt;%= query_with_warning.id %&gt;_trace&quot; class=&quot;indent small&quot;&gt;
+  &lt;% if QueryReviewer::CONFIGURATION[&quot;profiling&quot;] &amp;&amp; query_with_warning.profile %&gt;
+    &lt;div style=&quot;display: none&quot; id=&quot;warning_&lt;%= query_with_warning.id %&gt;_profile&quot; class=&quot;indent small tbpadded&quot;&gt;
+      &lt;%= render :partial =&gt; &quot;/profile&quot;, :locals =&gt; {:query =&gt; query_with_warning} %&gt;
+    &lt;/div&gt;
+  &lt;% end %&gt;
+  &lt;div style=&quot;display: none&quot; id=&quot;warning_&lt;%= query_with_warning.id %&gt;_trace&quot; class=&quot;indent small&quot;&gt;
 		&lt;%= render :partial =&gt; &quot;/trace&quot;, :object =&gt; query_with_warning.relevant_trace, :locals =&gt; {:query_id =&gt; query_with_warning.id, :full_trace =&gt; query_with_warning.full_trace} %&gt;
 	&lt;/div&gt;
 &lt;/li&gt;
\ No newline at end of file</diff>
      <filename>lib/query_reviewer/views/_query_with_warning.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -80,6 +80,16 @@ module QueryReviewer
       def enabled_by_cookie
         @controller.send(:cookies)[&quot;query_review_enabled&quot;]
       end
+
+      def duration_with_color(duration)        
+        if duration &gt; QueryReviewer::CONFIGURATION[&quot;critical_duration_threshold&quot;]
+          &quot;&lt;span style=\&quot;color: #{severity_color(9)}\&quot; title=\&quot;#{duration}\&quot;&gt;#{&quot;%.3f&quot; % duration}&lt;/span&gt;&quot;
+        elsif duration &gt; QueryReviewer::CONFIGURATION[&quot;warn_duration_threshold&quot;]
+          &quot;&lt;span style=\&quot;color: #{severity_color(QueryReviewer::CONFIGURATION[&quot;critical_severity&quot;])}\&quot; title=\&quot;#{duration}\&quot;&gt;#{&quot;%.3f&quot; % duration}&lt;/span&gt;&quot;
+        else
+          &quot;&lt;span title=\&quot;#{duration}\&quot;&gt;#{&quot;%.3f&quot; % duration}&lt;/span&gt;&quot;
+        end
+      end
     end
   end
 end</diff>
      <filename>lib/query_reviewer/views/query_review_box_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,10 +7,18 @@ all:
     trace_includes_vendor: false
     trace_includes_lib: true
     critical_query_count: 50
+    profiling: enabled
+    production_data: true
+    warn_duration_threshold: 0.2
+    critical_duration_threshold: 1.0
 
+# these values override the 'all' values when using that environment
 development:
     enabled: true
 
+staging:
+    enabled: true
+
 production:
     enabled: false
 </diff>
      <filename>query_reviewer_defaults.yml</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>bb89ad4fc57dab85281b1dda2518fdb298bd125d</id>
    </parent>
  </parents>
  <author>
    <name>David Stevenson</name>
    <email>david@flouri.sh</email>
  </author>
  <url>http://github.com/dsboulder/query_reviewer/commit/4de4ab74eaca847c59f2cde83cc6d36137e0c6b7</url>
  <id>4de4ab74eaca847c59f2cde83cc6d36137e0c6b7</id>
  <committed-date>2008-04-07T14:46:42-07:00</committed-date>
  <authored-date>2008-04-07T14:46:42-07:00</authored-date>
  <message>ds: added mysql query profiling and all related features, also added duration features</message>
  <tree>b6596cbc3aebc8463e02942d400fde014d179391</tree>
  <committer>
    <name>David Stevenson</name>
    <email>david@flouri.sh</email>
  </committer>
</commit>
