sql_safety_net 1.1.11 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,69 +3,111 @@ require 'spec_helper'
3
3
  describe SqlSafetyNet::QueryAnalysis do
4
4
 
5
5
  let(:analysis){ SqlSafetyNet::QueryAnalysis.new }
6
+ let(:query_info){ SqlSafetyNet::QueryInfo.new("SELECT * FROM *", :elapsed_time => 0.1, :rows => 2, :result_size => 500) }
6
7
 
7
- it "should add a flagged query" do
8
- analysis.add_query("sql", "test", 10, 0.01, :query_plan => "this sucks", :flags => ["write better sql"])
9
- analysis.non_flagged_queries.should be_empty
10
- query = analysis.flagged_queries.first
11
- query[:sql].should == "sql"
12
- query[:name].should == "test"
13
- query[:rows].should == 10
14
- query[:elapsed_time].should == 0.01
15
- query[:query_plan].should == "this sucks"
16
- query[:flags].should == ["write better sql"]
17
- query[:cached].should == false
8
+ it "should instantiate a query analysis inside a block" do
9
+ SqlSafetyNet::QueryAnalysis.current.should == nil
10
+ result = SqlSafetyNet::QueryAnalysis.capture do |analysis_1|
11
+ analysis_1.should be_a(SqlSafetyNet::QueryAnalysis)
12
+ SqlSafetyNet::QueryAnalysis.capture do |analysis_2|
13
+ analysis_2.should be_a(SqlSafetyNet::QueryAnalysis)
14
+ "hello"
15
+ end
16
+ end
17
+ SqlSafetyNet::QueryAnalysis.current.should == nil
18
+ result.should == "hello"
18
19
  end
19
20
 
20
- it "should add a non-flagged query" do
21
- analysis.add_query("sql", "test", 10, 0.01, nil)
22
- analysis.flagged_queries.should be_empty
23
- query = analysis.non_flagged_queries.first
24
- query[:sql].should == "sql"
25
- query[:name].should == "test"
26
- query[:rows].should == 10
27
- query[:elapsed_time].should == 0.01
28
- query[:cached].should == false
21
+ it "should track the number of queries" do
22
+ analysis.queries.should == []
23
+ analysis.total_queries.should == 0
24
+ analysis << query_info
25
+ analysis.queries.should == [query_info]
26
+ analysis.total_queries.should == 1
27
+ analysis << query_info
28
+ analysis.queries.should == [query_info, query_info]
29
+ analysis.total_queries.should == 2
29
30
  end
30
31
 
31
- it "should determine if any query is flagged" do
32
- analysis.add_query("sql", "test", 10, 0.01, :query_plan => "this sucks", :flags => ["write better sql"])
33
- analysis.add_query("sql", "test", 10, 0.01, nil)
34
- analysis.should be_flagged
35
- analysis.flagged_queries?.should == true
32
+ it "should track the elasped time of all queries" do
33
+ analysis.elapsed_time.should == 0.0
34
+ analysis << query_info
35
+ analysis.elapsed_time.should == 0.1
36
+ analysis << query_info
37
+ analysis.elapsed_time.should == 0.2
36
38
  end
37
39
 
38
- it "should determine if the rows selected is flagged" do
39
- analysis.rows = 1000000
40
- analysis.should be_flagged
41
- analysis.too_many_rows?.should == true
40
+ it "should track the rows of all queries" do
41
+ analysis.rows.should == 0
42
+ analysis << query_info
43
+ analysis.rows.should == 2
44
+ analysis << query_info
45
+ analysis.rows.should == 4
42
46
  end
43
47
 
44
- it "should determine if the number of selects is flagged" do
45
- analysis.selects = 100000
46
- analysis.should be_flagged
47
- analysis.too_many_selects?.should == true
48
+ it "should track the result size of all queries" do
49
+ analysis.result_size.should == 0
50
+ analysis << query_info
51
+ analysis.result_size.should == 500
52
+ analysis << query_info
53
+ analysis.result_size.should == 1000
48
54
  end
49
55
 
50
- it "should set the analysis object within a block" do
51
- SqlSafetyNet::QueryAnalysis.analyze do
52
- SqlSafetyNet::QueryAnalysis.current.should be_a(SqlSafetyNet::QueryAnalysis)
56
+ describe "flags" do
57
+ it "should determine the number of queries that have alerts" do
58
+ analysis.alerted_queries.should == 0
59
+ analysis << query_info
60
+ analysis.alerted_queries.should == 0
61
+ analysis << SqlSafetyNet::QueryInfo.new("SELECT *", :alerts => ["boom"])
62
+ analysis.alerted_queries.should == 1
53
63
  end
54
- SqlSafetyNet::QueryAnalysis.current.should == nil
55
- end
56
-
57
- it "should determine if a query is happening in a cache block" do
58
- cache = ActiveSupport::Cache::MemoryStore.new
59
- analysis = nil
60
- SqlSafetyNet::QueryAnalysis.analyze do
61
- val = cache.fetch("key") do
62
- analysis = SqlSafetyNet::QueryAnalysis.current
63
- analysis.add_query("sql", "test", 10, 0.01, nil)
64
- "woot"
64
+
65
+ it "should determine if any queries have alerts" do
66
+ analysis.alerts?.should == false
67
+ analysis << query_info
68
+ analysis.alerts?.should == false
69
+ analysis << SqlSafetyNet::QueryInfo.new("SELECT *", :alerts => ["boom"])
70
+ analysis.alerts?.should == true
71
+ end
72
+
73
+ it "should determine if too many queries have been made" do
74
+ SqlSafetyNet.override_config do |config|
75
+ config.query_limit = 1
76
+ analysis << query_info
77
+ analysis.too_many_queries?.should == false
78
+ analysis << query_info
79
+ analysis.too_many_queries?.should == true
80
+ end
81
+ end
82
+
83
+ it "should determine if too many rows have been returned" do
84
+ SqlSafetyNet.override_config do |config|
85
+ config.returned_rows_limit = 3
86
+ analysis << query_info
87
+ analysis.too_many_rows?.should == false
88
+ analysis << query_info
89
+ analysis.too_many_rows?.should == true
90
+ end
91
+ end
92
+
93
+ it "should determine if the results are too big" do
94
+ SqlSafetyNet.override_config do |config|
95
+ config.result_size_limit = 600
96
+ analysis << query_info
97
+ analysis.results_too_big?.should == false
98
+ analysis << query_info
99
+ analysis.results_too_big?.should == true
100
+ end
101
+ end
102
+
103
+ it "should determine if the queries take too much time" do
104
+ SqlSafetyNet.override_config do |config|
105
+ config.elapsed_time_limit = 0.15
106
+ analysis << query_info
107
+ analysis.too_much_time?.should == false
108
+ analysis << query_info
109
+ analysis.too_much_time?.should == true
65
110
  end
66
- val.should == "woot"
67
111
  end
68
- query = analysis.non_flagged_queries.first
69
- query[:cached].should == true
70
112
  end
71
113
  end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe SqlSafetyNet::QueryInfo do
4
+
5
+ describe "constructor" do
6
+ it "should keep the sql passed in to the constructor" do
7
+ query_info = SqlSafetyNet::QueryInfo.new("SELECT * FROM *")
8
+ query_info.sql.should == "SELECT * FROM *"
9
+ end
10
+
11
+ it "should keep the elapsed time passed in :elapsed_time option" do
12
+ query_info = SqlSafetyNet::QueryInfo.new("SELECT * FROM *", :elapsed_time => 0.1)
13
+ query_info.elapsed_time.should == 0.1
14
+ end
15
+
16
+ it "should keep the rows passed in :rows option" do
17
+ query_info = SqlSafetyNet::QueryInfo.new("SELECT * FROM *", :rows => 10)
18
+ query_info.rows.should == 10
19
+ end
20
+
21
+ it "should keep the result_size passed in :result_size option" do
22
+ query_info = SqlSafetyNet::QueryInfo.new("SELECT * FROM *", :result_size => 100)
23
+ query_info.result_size.should == 100
24
+ end
25
+
26
+ it "should keep the cached value passed in :cached option" do
27
+ query_info = SqlSafetyNet::QueryInfo.new("SELECT * FROM *", :cached => true)
28
+ query_info.cached?.should == true
29
+ end
30
+ end
31
+
32
+ describe "alerts" do
33
+ it "should not have any alerts by default" do
34
+ query_info = SqlSafetyNet::QueryInfo.new("SELECT * FROM *")
35
+ query_info.alerts.should == []
36
+ end
37
+
38
+ it "should indicate if it has any alerts" do
39
+ query_info = SqlSafetyNet::QueryInfo.new("SELECT * FROM *")
40
+ query_info.alerts?.should == false
41
+ query_info.alerts << "problem"
42
+ query_info.alerts?.should == true
43
+ end
44
+ end
45
+
46
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,15 +1,12 @@
1
1
  require 'rubygems'
2
2
 
3
- active_record_version = ENV['ACTIVE_RECORD_VERSION'] || ">=2.2.2"
3
+ active_record_version = ENV['ACTIVE_RECORD_VERSION'] || ">=3.2.0"
4
4
  gem 'rails', active_record_version
5
5
  gem 'activerecord', active_record_version
6
6
  gem 'activesupport', active_record_version
7
7
  require 'active_support/all'
8
8
  require 'active_record'
9
- puts "Testing against #{ActiveRecord::VERSION::STRING}"
10
-
11
- require 'mysql'
12
- require 'pg'
9
+ puts "Testing against activerecord #{ActiveRecord::VERSION::STRING}"
13
10
 
14
11
  begin
15
12
  require 'simplecov'
@@ -24,33 +21,17 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'sql_saf
24
21
 
25
22
  module SqlSafetyNet
26
23
  class TestModel < ActiveRecord::Base
27
- def self.create_tables
24
+ def self.create_table
28
25
  connection.create_table(table_name) do |t|
29
26
  t.string :name
27
+ t.integer :value
30
28
  end unless table_exists?
31
29
  end
32
-
33
- def self.drop_tables
34
- connection.drop_table(table_name)
35
- end
36
-
37
- def self.database_config
38
- database_yml = File.expand_path(File.join(File.dirname(__FILE__), 'database.yml'))
39
- raise "You must create a database.yml file in the spec directory (see example_database.yml)" unless File.exist?(database_yml)
40
- YAML.load_file(database_yml)
41
- end
42
- end
43
-
44
- class MysqlTestModel < TestModel
45
- establish_connection(database_config['mysql'])
46
- end
47
-
48
- class PostgresqlTestModel < TestModel
49
- establish_connection(database_config['postgresql'])
50
30
  end
51
31
 
52
- SqlSafetyNet.config.enable_on(SqlSafetyNet::MysqlTestModel.connection.class)
53
- SqlSafetyNet.config.enable_on(SqlSafetyNet::PostgresqlTestModel.connection.class)
32
+ TestModel.establish_connection(:adapter => "sqlite3", :database => ":memory:")
33
+ TestModel.create_table
54
34
  end
55
35
 
56
36
  ActiveSupport::Cache::Store.send(:include, SqlSafetyNet::CacheStore)
37
+ SqlSafetyNet.enable_on_connection_adapter!(SqlSafetyNet::TestModel.connection.class)
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe SqlSafetyNet do
4
+
5
+ it "should have a singleton config" do
6
+ config = SqlSafetyNet.config
7
+ config.should be_a(SqlSafetyNet::Configuration)
8
+ config.object_id.should == SqlSafetyNet.config.object_id
9
+ end
10
+
11
+ it "should be able to override the config in a block" do
12
+ original_config = SqlSafetyNet.config
13
+ original_style = original_config.style.dup
14
+ SqlSafetyNet.override_config do |config|
15
+ config.object_id.should_not == original_config.object_id
16
+ config.style = {"top" => "5px"}
17
+ end
18
+ original_config.style.should == original_style
19
+ end
20
+
21
+ end
metadata CHANGED
@@ -1,102 +1,111 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: sql_safety_net
3
- version: !ruby/object:Gem::Version
4
- version: 1.1.11
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
5
  prerelease:
6
+ segments:
7
+ - 2
8
+ - 0
9
+ - 0
10
+ version: 2.0.0
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Brian Durand
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2012-06-06 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
17
+
18
+ date: 2012-06-14 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
15
21
  name: activesupport
16
- requirement: &70157641410240 !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
17
24
  none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 7
29
+ segments:
30
+ - 3
31
+ - 0
32
+ - 0
33
+ version: 3.0.0
22
34
  type: :runtime
23
- prerelease: false
24
- version_requirements: *70157641410240
25
- - !ruby/object:Gem::Dependency
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
26
37
  name: activerecord
27
- requirement: &70157641409720 !ruby/object:Gem::Requirement
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
28
40
  none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: 2.2.2
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 7
45
+ segments:
46
+ - 3
47
+ - 0
48
+ - 0
49
+ version: 3.0.0
33
50
  type: :runtime
34
- prerelease: false
35
- version_requirements: *70157641409720
36
- - !ruby/object:Gem::Dependency
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
37
53
  name: actionpack
38
- requirement: &70157641409100 !ruby/object:Gem::Requirement
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
39
56
  none: false
40
- requirements:
41
- - - ! '>='
42
- - !ruby/object:Gem::Version
43
- version: '0'
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 7
61
+ segments:
62
+ - 3
63
+ - 0
64
+ - 0
65
+ version: 3.0.0
44
66
  type: :runtime
45
- prerelease: false
46
- version_requirements: *70157641409100
47
- - !ruby/object:Gem::Dependency
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
48
69
  name: rspec
49
- requirement: &70157641408520 !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: 2.0.0
55
- type: :development
56
- prerelease: false
57
- version_requirements: *70157641408520
58
- - !ruby/object:Gem::Dependency
59
- name: mysql
60
- requirement: &70157641408000 !ruby/object:Gem::Requirement
61
- none: false
62
- requirements:
63
- - - ! '>='
64
- - !ruby/object:Gem::Version
65
- version: '0'
66
- type: :development
67
70
  prerelease: false
68
- version_requirements: *70157641408000
69
- - !ruby/object:Gem::Dependency
70
- name: pg
71
- requirement: &70157641407460 !ruby/object:Gem::Requirement
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
72
  none: false
73
- requirements:
74
- - - ! '>='
75
- - !ruby/object:Gem::Version
76
- version: '0'
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 15
77
+ segments:
78
+ - 2
79
+ - 0
80
+ - 0
81
+ version: 2.0.0
77
82
  type: :development
78
- prerelease: false
79
- version_requirements: *70157641407460
80
- - !ruby/object:Gem::Dependency
83
+ version_requirements: *id004
84
+ - !ruby/object:Gem::Dependency
81
85
  name: sqlite3-ruby
82
- requirement: &70157641406900 !ruby/object:Gem::Requirement
86
+ prerelease: false
87
+ requirement: &id005 !ruby/object:Gem::Requirement
83
88
  none: false
84
- requirements:
85
- - - ! '>='
86
- - !ruby/object:Gem::Version
87
- version: '0'
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ hash: 3
93
+ segments:
94
+ - 0
95
+ version: "0"
88
96
  type: :development
89
- prerelease: false
90
- version_requirements: *70157641406900
97
+ version_requirements: *id005
91
98
  description: Debug SQL statements in ActiveRecord by displaying warnings on bad queries.
92
- email:
99
+ email:
93
100
  - mdobrota@tribune.com
94
101
  - ddpr@tribune.com
95
102
  executables: []
103
+
96
104
  extensions: []
97
- extra_rdoc_files:
105
+
106
+ extra_rdoc_files:
98
107
  - README.rdoc
99
- files:
108
+ files:
100
109
  - License.txt
101
110
  - README.rdoc
102
111
  - Rakefile
@@ -104,45 +113,59 @@ files:
104
113
  - lib/sql_safety_net/cache_store.rb
105
114
  - lib/sql_safety_net/configuration.rb
106
115
  - lib/sql_safety_net/connection_adapter.rb
107
- - lib/sql_safety_net/connection_adapter/mysql_adapter.rb
108
- - lib/sql_safety_net/connection_adapter/postgresql_adapter.rb
116
+ - lib/sql_safety_net/explain_plan.rb
117
+ - lib/sql_safety_net/explain_plan/mysql.rb
118
+ - lib/sql_safety_net/explain_plan/postgresql.rb
119
+ - lib/sql_safety_net/formatter.rb
120
+ - lib/sql_safety_net/middleware.rb
109
121
  - lib/sql_safety_net/query_analysis.rb
110
- - lib/sql_safety_net/rack_handler.rb
122
+ - lib/sql_safety_net/query_info.rb
111
123
  - spec/cache_store_spec.rb
112
124
  - spec/configuration_spec.rb
113
125
  - spec/connection_adapter_spec.rb
114
- - spec/example_database.yml
115
- - spec/mysql_connection_adapter_spec.rb
116
- - spec/postgres_connection_adapter_spec.rb
126
+ - spec/explain_plan/mysql_spec.rb
127
+ - spec/explain_plan/postgresql_spec.rb
128
+ - spec/formatter_spec.rb
129
+ - spec/middleware_spec.rb
117
130
  - spec/query_analysis_spec.rb
118
- - spec/rack_handler_spec.rb
131
+ - spec/query_info_spec.rb
119
132
  - spec/spec_helper.rb
133
+ - spec/sql_safety_net_spec.rb
120
134
  homepage:
121
135
  licenses: []
136
+
122
137
  post_install_message:
123
- rdoc_options:
138
+ rdoc_options:
124
139
  - --line-numbers
125
140
  - --inline-source
126
141
  - --main
127
142
  - README.rdoc
128
- require_paths:
143
+ require_paths:
129
144
  - lib
130
- required_ruby_version: !ruby/object:Gem::Requirement
145
+ required_ruby_version: !ruby/object:Gem::Requirement
131
146
  none: false
132
- requirements:
133
- - - ! '>='
134
- - !ruby/object:Gem::Version
135
- version: '0'
136
- required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ hash: 3
151
+ segments:
152
+ - 0
153
+ version: "0"
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
155
  none: false
138
- requirements:
139
- - - ! '>='
140
- - !ruby/object:Gem::Version
141
- version: '0'
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ hash: 3
160
+ segments:
161
+ - 0
162
+ version: "0"
142
163
  requirements: []
164
+
143
165
  rubyforge_project:
144
166
  rubygems_version: 1.8.10
145
167
  signing_key:
146
168
  specification_version: 3
147
169
  summary: Debug SQL statements in ActiveRecord
148
170
  test_files: []
171
+
@@ -1,43 +0,0 @@
1
- module SqlSafetyNet
2
- module ConnectionAdapter
3
- # Logic for analyzing MySQL query plans.
4
- module MysqlAdapter
5
- def analyze_query(sql, name, *args)
6
- if select_statement?(sql)
7
- query_plan = select_without_sql_safety_net("EXPLAIN #{sql}", "EXPLAIN", *args)
8
- query_plan_flags = analyze_query_plan(query_plan)
9
- unless query_plan_flags.empty?
10
- @logger.debug("Flagged query plan #{name} (#{query_plan_flags.join(', ')}): #{query_plan.inspect}") if @logger
11
- return {:query_plan => query_plan, :flags => query_plan_flags}
12
- end
13
- end
14
- end
15
-
16
- private
17
-
18
- def analyze_query_plan(query_plan)
19
- flagged = []
20
- query_plan.each do |row|
21
- select_type = (row['select_type'] || '').downcase
22
- type = (row['type'] || '').downcase
23
- rows = row['rows'].to_i
24
- extra = (row['Extra'] || '').downcase
25
- key = row['key']
26
- possible_keys = row['possible_keys']
27
-
28
- flagged << 'table scan' if (type.include?('all') and rows > SqlSafetyNet.config.table_scan_limit)
29
- flagged << 'fulltext search' if type.include?('fulltext')
30
- flagged << 'no index used' if (key.blank? and rows > SqlSafetyNet.config.table_scan_limit)
31
- flagged << 'no indexes possible' if (possible_keys.blank? and rows > SqlSafetyNet.config.table_scan_limit)
32
- flagged << 'dependent subquery' if select_type.include?('dependent')
33
- flagged << 'uncacheable subquery' if select_type.include?('uncacheable')
34
- flagged << 'full scan on null key' if extra.include?('full scan on null key')
35
- flagged << "uses temporary table for #{rows} rows" if extra.include?('using temporary') and rows > SqlSafetyNet.config.temporary_table_limit
36
- flagged << "uses filesort for #{rows} rows" if extra.include?('filesort') and rows > SqlSafetyNet.config.filesort_limit
37
- flagged << "examines #{rows} rows" if rows > SqlSafetyNet.config.examine_rows_limit
38
- end
39
- return flagged
40
- end
41
- end
42
- end
43
- end
@@ -1,48 +0,0 @@
1
- module SqlSafetyNet
2
- module ConnectionAdapter
3
- # Logic for analyzing a query plan from PostgreSQL. These plans are not terribly useful and sometimes
4
- # the statistics are off, so take them with a grain of salt.
5
- module PostgreSQLAdapter
6
- def analyze_query(sql, name, *args)
7
- if select_statement?(sql)
8
- query_plan = select_without_sql_safety_net("EXPLAIN #{sql}", "EXPLAIN", *args)
9
- query_plan_flags = analyze_query_plan(query_plan)
10
- unless query_plan_flags.empty?
11
- @logger.debug("Flagged query plan #{name} (#{query_plan_flags.join(', ')}): #{query_plan.inspect}") if @logger
12
- return {:query_plan => query_plan, :flags => query_plan_flags}
13
- end
14
- end
15
- end
16
-
17
- private
18
-
19
- def analyze_query_plan(query_plan)
20
- query_plan = query_plan.collect{|r| r.values.first}
21
- flagged = []
22
- limit = nil
23
- query_plan.each do |row|
24
- row_count = query_plan_rows_value(row)
25
- row_count = [limit, row_count].min if limit
26
- if row =~ /^(\s|(->))*Limit\s/
27
- limit = row_count
28
- elsif row =~ /^(\s|(->))*Seq Scan/
29
- flagged << 'table scan' if row_count > SqlSafetyNet.config.table_scan_limit
30
- elsif row_count > SqlSafetyNet.config.examine_rows_limit
31
- flagged << "examines #{row_count} rows"
32
- end
33
- end
34
- return flagged
35
- end
36
-
37
- private
38
-
39
- def query_plan_rows_value(plan_row)
40
- if plan_row.match(/\brows=(\d+)/)
41
- return $~[1].to_i
42
- else
43
- return 0
44
- end
45
- end
46
- end
47
- end
48
- end