shiba 0.5.0 → 0.6.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.
@@ -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