shiba 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,117 @@
1
+ require 'json'
2
+ require 'open3'
3
+
4
+ require 'shiba/review/diff'
5
+ require 'shiba/backtrace'
6
+
7
+ module Shiba
8
+ module Review
9
+ # Given an explain log and a diff, returns any explain logs
10
+ # that appear to be caused by the diff.
11
+ class ExplainDiff
12
+ Result = Struct.new(:status, :message)
13
+
14
+ attr_reader :options
15
+
16
+ def initialize(log, options)
17
+ @log = log
18
+ @options = options
19
+ end
20
+
21
+ def diff_requested_by_user?
22
+ [ "staged", "unstaged", "branch", "diff" ].any? { |key| @options.key?(key) }
23
+ end
24
+
25
+ # Returns detected problem queries with their line numbers.
26
+ # Query problem format is [ [ "path:lineno", explain ]... ]
27
+ def problems
28
+ return @problems if @problems
29
+
30
+ @problems = explains_with_backtrace_in_diff.select do |explain|
31
+ explain["severity"] && explain["severity"] != 'none'
32
+ end
33
+
34
+ if options["verbose"]
35
+ $stderr.puts @problems
36
+ $stderr.puts "Updated lines: #{updated_lines}"
37
+ end
38
+
39
+ @problems.map! do |problem|
40
+ line = diff_line_from_backtrace(problem["backtrace"])
41
+ next if line.nil?
42
+
43
+ [ line, problem ]
44
+ end
45
+ @problems.compact!
46
+
47
+ @problems
48
+ end
49
+
50
+ def result
51
+ msg = nil
52
+
53
+ if changed_files.empty?
54
+ if options['verbose']
55
+ msg = "No changes found. Are you sure you specified the correct branch?"
56
+ end
57
+ return Result.new(:pass, msg)
58
+ end
59
+
60
+ if problems.empty?
61
+ msg = "No problems found caused by the diff"
62
+ return Result.new(:pass, msg)
63
+ end
64
+
65
+ return Result.new(:fail, "Potential problems")
66
+ end
67
+
68
+ protected
69
+
70
+ # file.rb:32:in `hello'",
71
+ LINE_NUMBER_PATTERN = /:(\d+):/
72
+
73
+ def diff_line_from_backtrace(backtrace)
74
+ backtrace.each do |bl|
75
+ updated_lines.each do |path, lines|
76
+ next if !bl.start_with?(path)
77
+ bl =~ LINE_NUMBER_PATTERN
78
+ next if !lines.include?($1.to_i)
79
+
80
+ return "#{path}:#{$1}"
81
+ end
82
+ end
83
+
84
+ return nil
85
+ end
86
+
87
+ # All explains from the log file with a backtrace that contains a changed file.
88
+ def explains_with_backtrace_in_diff
89
+ patterns = changed_files.map { |path| "-e #{path}" }.join(" ")
90
+ cmd = "grep #{@log} #{patterns}"
91
+ $stderr.puts cmd if options["verbose"]
92
+
93
+ json_lines = `#{cmd}`
94
+ json_lines.each_line.map { |line| JSON.parse(line) }
95
+ end
96
+
97
+ def changed_files
98
+ @changed_files ||= diff.paths
99
+ end
100
+
101
+ def updated_lines
102
+ return @updated_lines if @updated_lines
103
+
104
+ diff_file = diff.file(context: 0, ignore_deletions: true)
105
+ @updated_lines = Diff::Parser.new(diff_file).updated_lines
106
+ @updated_lines.map! do |path, lines|
107
+ [ Shiba::Backtrace.clean!(path), lines ]
108
+ end
109
+ end
110
+
111
+ def diff
112
+ @diff ||= options["diff"] ? Diff::FileDiff.new(options["diff"]) : Diff::GitDiff.new(options)
113
+ end
114
+
115
+ end
116
+ end
117
+ end
@@ -1,6 +1,6 @@
1
1
  require 'open3'
2
2
  require 'shiba'
3
- require 'shiba/diff'
3
+ require 'shiba/review/diff'
4
4
  require 'shiba/review/api'
5
5
  require 'shiba/review/comment_renderer'
6
6
 
@@ -10,7 +10,6 @@ module Shiba
10
10
  # 2. May make sense to edit the comment on a commit line when the code
11
11
  # is semi-corrected but still a problem
12
12
  class Reviewer
13
- TEMPLATE_FILE = File.join(Shiba.root, 'lib/shiba/output/tags.yaml')
14
13
  MESSAGE_FILTER_THRESHOLD = 0.005
15
14
 
16
15
  attr_reader :repo_url, :problems, :options
@@ -20,7 +19,9 @@ module Shiba
20
19
  @problems = problems
21
20
  @options = options
22
21
  @commit_id = options.fetch("branch") do
23
- raise Shiba::Error.new("Must specify a branch") if !options['diff']
22
+ if options["submit"]
23
+ raise Shiba::Error.new("Must specify a branch")
24
+ end
24
25
  end
25
26
  end
26
27
 
@@ -33,7 +34,11 @@ module Shiba
33
34
  raise Shiba::Error.new("Bad path received: #{line_number}")
34
35
  end
35
36
 
36
- position = diff.find_position(file, line_number.to_i)
37
+ position = if path == "none:-1"
38
+ nil
39
+ else
40
+ diff_parser.find_position(file, line_number.to_i)
41
+ end
37
42
 
38
43
  explain = keep_only_dangerous_messages(explain)
39
44
 
@@ -97,22 +102,18 @@ module Shiba
97
102
  explain_b
98
103
  end
99
104
 
100
- def diff
101
- return @diff if @diff
102
- output = options['diff'] ? file_diff : git_diff
103
- @diff = Shiba::Diff.new(output)
104
- end
105
-
106
- def git_diff
107
- cmd ="git diff origin/HEAD..#{@commit_id}"
108
- report("Finding PR position using: #{cmd}")
109
-
110
- output = StringIO.new(`#{cmd}`)
105
+ def diff_parser
106
+ @diff_parser ||= Review::Diff::Parser.new(diff.file)
111
107
  end
112
108
 
113
- def file_diff
114
- report("Finding PR position using file: #{options['diff']}")
115
- File.open(options['diff'], 'r')
109
+ def diff
110
+ @diff ||= if options['diff']
111
+ report("Finding PR position using file: #{options['diff']}")
112
+ Review::Diff::FileDiff.new(options['diff'])
113
+ else
114
+ report("Finding PR position using git")
115
+ Review::Diff::GitDiff.new(options)
116
+ end
116
117
  end
117
118
 
118
119
  def api
@@ -130,7 +131,7 @@ module Shiba
130
131
  end
131
132
 
132
133
  def tags
133
- @tags ||= YAML.load_file(TEMPLATE_FILE)
134
+ @tags ||= YAML.load_file(Shiba::TEMPLATE_FILE)
134
135
  end
135
136
 
136
137
  end
@@ -18,6 +18,10 @@ module Shiba
18
18
  ask_each(:table_count, table)
19
19
  end
20
20
 
21
+ def get_column_size(table_name, column)
22
+ ask_each(:get_column_size, table_name, column)
23
+ end
24
+
21
25
  def fuzzed?(table)
22
26
  !@dump_stats.tables[table] && !@manual_stats.tables[table] && @db_stats.tables[table]
23
27
  end
data/lib/shiba/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Shiba
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
data/web/results.html.erb CHANGED
@@ -206,6 +206,20 @@
206
206
  return (this.running_cost * 100).toFixed() + "ms";
207
207
  else
208
208
  return this.running_cost.toFixed(1) + "s";
209
+ },
210
+ formatted_result: function() {
211
+ var rb = this.result_bytes;
212
+ var result;
213
+ if ( rb == 0 )
214
+ return "" + this.result_size + " rows";
215
+ else if ( rb < 1000 )
216
+ result = rb + " bytes ";
217
+ else if ( rb < 1000000 )
218
+ result = (rb / 1000).toFixed() + "kb ";
219
+ else
220
+ result = (rb / 1000000 ).toFixed(1) + "mb ";
221
+
222
+ return result + " (" + this.result_size.toLocaleString() + " rows)";
209
223
  }
210
224
  }
211
225
  </script>
@@ -226,7 +240,7 @@
226
240
  <script>
227
241
  Vue.component('tag-<%= tag %>', {
228
242
  template: '#tag-<%= tag %>-template',
229
- props: [ 'table_size', 'result_size', 'table', 'cost', 'index', 'join_to', 'index_used', 'running_cost', 'tables', 'rows_read' ],
243
+ props: [ 'table_size', 'result_size', 'table', 'cost', 'index', 'join_to', 'index_used', 'running_cost', 'tables', 'rows_read', 'result_bytes' ],
230
244
  computed: templateComputedFunctions,
231
245
  data: function () {
232
246
  return { lastRunnningCost: undefined };
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shiba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Osheroff
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-03-06 00:00:00.000000000 Z
12
+ date: 2019-03-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -121,12 +121,11 @@ files:
121
121
  - lib/shiba/activerecord_integration.rb
122
122
  - lib/shiba/analyzer.rb
123
123
  - lib/shiba/backtrace.rb
124
- - lib/shiba/checker.rb
125
124
  - lib/shiba/configure.rb
126
125
  - lib/shiba/connection.rb
127
126
  - lib/shiba/connection/mysql.rb
128
127
  - lib/shiba/connection/postgres.rb
129
- - lib/shiba/diff.rb
128
+ - lib/shiba/console.rb
130
129
  - lib/shiba/explain.rb
131
130
  - lib/shiba/explain/check_support.rb
132
131
  - lib/shiba/explain/checks.rb
@@ -139,10 +138,15 @@ files:
139
138
  - lib/shiba/index_stats.rb
140
139
  - lib/shiba/output.rb
141
140
  - lib/shiba/output/tags.yaml
141
+ - lib/shiba/parsers/mysql_select_fields.rb
142
+ - lib/shiba/parsers/shiba_string_scanner.rb
142
143
  - lib/shiba/query.rb
143
144
  - lib/shiba/query_watcher.rb
144
145
  - lib/shiba/review/api.rb
146
+ - lib/shiba/review/cli.rb
145
147
  - lib/shiba/review/comment_renderer.rb
148
+ - lib/shiba/review/diff.rb
149
+ - lib/shiba/review/explain_diff.rb
146
150
  - lib/shiba/reviewer.rb
147
151
  - lib/shiba/setup.rb
148
152
  - lib/shiba/table_stats.rb
@@ -177,7 +181,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
181
  - !ruby/object:Gem::Version
178
182
  version: '0'
179
183
  requirements: []
180
- rubygems_version: 3.0.1
184
+ rubyforge_project:
185
+ rubygems_version: 2.7.6
181
186
  signing_key:
182
187
  specification_version: 4
183
188
  summary: A gem that attempts to find bad queries before you shoot self in foot
data/lib/shiba/checker.rb DELETED
@@ -1,165 +0,0 @@
1
- require 'json'
2
- require 'open3'
3
-
4
- require 'shiba/diff'
5
- require 'shiba/backtrace'
6
-
7
- module Shiba
8
- # Given an explain log and a diff, returns any explain logs
9
- # that appear to be caused by the diff.
10
- class Checker
11
- Result = Struct.new(:status, :message, :problems)
12
-
13
- attr_reader :options
14
-
15
- def initialize(options)
16
- @options = options
17
- end
18
-
19
- # Returns a Result object with a status, message, and any problem queries detected.
20
- # Query problem format is [ [ "path:lineno", explain ]... ]
21
- def run(log)
22
- msg = nil
23
-
24
- if options['verbose']
25
- puts cmd
26
- end
27
-
28
- if changed_files.empty?
29
- if options['verbose']
30
- msg = "No changes found. Are you sure you specified the correct branch?"
31
- end
32
- return Result.new(:pass, msg)
33
- end
34
-
35
- explains = select_lines_with_changed_files(log)
36
- problems = explains.select { |explain| explain["severity"] && explain["severity"] != 'none' }
37
-
38
-
39
- if options["verbose"]
40
- puts problems
41
- puts "Updated lines: #{updated_lines}"
42
- end
43
-
44
- if problems.empty?
45
- msg = "No problems found caused by the diff"
46
- return Result.new(:pass, msg)
47
- end
48
-
49
- problems.map! do |problem|
50
- line = updated_line_from_backtrace(problem["backtrace"], updated_lines)
51
- next if line.nil?
52
-
53
- [ line, problem ]
54
- end
55
- problems.compact!
56
-
57
- if problems.empty?
58
- msg = "No problems found caused by the diff"
59
- return Result.new(:pass, msg)
60
- end
61
-
62
- return Result.new(:fail, "Potential problems", problems)
63
- end
64
-
65
- protected
66
-
67
- def updated_line_from_backtrace(backtrace, updates)
68
- backtrace.each do |bl|
69
- updates.each do |path, lines|
70
- next if !bl.start_with?(path)
71
- bl =~ /:(\d+):/
72
- next if !lines.include?($1.to_i)
73
-
74
- return "#{path}:#{$1}"
75
- end
76
- end
77
-
78
- return nil
79
- end
80
-
81
- def select_lines_with_changed_files(log)
82
- patterns = changed_files.map { |path| "-e #{path}" }.join(" ")
83
- cmd = "grep #{log} #{patterns}"
84
- $stderr.puts cmd if options["verbose"]
85
-
86
- json_lines = `#{cmd}`
87
- json_lines.each_line.map { |line| JSON.parse(line) }
88
- end
89
-
90
- def changed_files
91
- @changed_files ||= begin
92
- options['diff'] ? file_diff_names : git_diff_names
93
- end
94
- end
95
-
96
- def updated_lines
97
- return @updated_lines if @updated_lines
98
-
99
-
100
- out = options['diff'] ? file_diff_lines : git_diff_lines
101
- @updated_lines = Shiba::Diff.new(out).updated_lines
102
-
103
-
104
- @updated_lines.map! do |path, lines|
105
- [ Shiba::Backtrace.clean!(path), lines ]
106
- end
107
- end
108
-
109
- def file_diff_lines
110
- File.open(options['diff'])
111
- end
112
-
113
- def git_diff_lines
114
- run = "git diff#{cmd} --unified=0 --diff-filter=d"
115
- if options[:verbose]
116
- $stderr.puts run
117
- end
118
-
119
- _, out,_,_ = Open3.popen3(run)
120
- out
121
- end
122
-
123
- # index ade9b24..661d522 100644
124
- # --- a/test/app/app.rb
125
- # +++ b/test/app/app.rb
126
- # @@ -24,4 +24,4 @@ ActiveRecord::Base...
127
- # org = Organization.create!(name: 'test')
128
- #
129
- # file_diff_lines
130
- # => test/app/app.rb
131
- def file_diff_names
132
- file_name_pattern = /^\+\+\+ b\/(.*?)$/
133
- f = File.open(options['diff'])
134
- f.grep(file_name_pattern) { $1 }
135
- end
136
-
137
- def git_diff_names
138
- run = "git diff#{cmd} --name-only --diff-filter=d"
139
-
140
- if options[:verbose]
141
- $stderr.puts run
142
- end
143
- result = `#{run}`
144
- if $?.exitstatus != 0
145
- $stderr.puts result
146
- raise Shiba::Error.new "Failed to read changes"
147
- end
148
-
149
- result.split("\n")
150
- end
151
-
152
- def cmd
153
- cmd = case
154
- when options["staged"]
155
- " --staged"
156
- when options["unstaged"]
157
- ""
158
- else
159
- commit = " origin/HEAD"
160
- commit << "...#{options["branch"]}" if options["branch"]
161
- commit
162
- end
163
- end
164
- end
165
- end