Module: Testoscope
- Defined in:
- lib/testoscope.rb,
lib/testoscope/version.rb
Defined Under Namespace
Modules: AdapterUpgrade
Classes: Config
Constant Summary
collapse
- VERSION =
"0.2.0"
Class Method Summary
collapse
Class Method Details
.add_index_used(sql, explain, index_used) ⇒ Object
47
48
49
50
|
# File 'lib/testoscope.rb', line 47
def self.add_index_used( sql, explain, index_used )
results[:indexes][index_used] ||= []
results[:indexes][index_used] << { explain: explain, sql: sql }
end
|
.add_unintended_behaviour(sql, explain, backtrace) ⇒ Object
39
40
41
42
43
44
|
# File 'lib/testoscope.rb', line 39
def self.add_unintended_behaviour( sql, explain, backtrace )
results[:unintended_behaviour][sql] ||= {}
results[:unintended_behaviour][sql][:explain] = explain
results[:unintended_behaviour][sql][:backtrace] ||= []
results[:unintended_behaviour][sql][:backtrace] << backtrace.to_a unless results[:unintended_behaviour][sql][:backtrace].include?(backtrace)
end
|
.analyze(sql) ⇒ Object
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
# File 'lib/testoscope.rb', line 96
def self.analyze( sql )
if config.analyze && sql_has_analyzing_tables?(sql)
explain = yield
explain = config.pp_class.method(:pp).arity.abs == 1 ? config.pp_class.new.pp( explain ) : config.pp_class.new.pp( explain, 0 )
app_trace = caller_locations( 2 ).map(&:to_s).select { |st|
self.config.back_trace_paths.any?{|pth| st[pth]} && !self.config.back_trace_exclude_paths.any?{|epth| st[epth]}
}
return if app_trace.length == 0
unintended_found = self.config.unintened_key_words.select{|ukw| explain[ukw] }
if unintended_found.length > 0
raise StandardError.new("#{unintended_found.join(', ')} found!\n #{explain}") if config.raise_when_unintended
self.add_unintended_behaviour( sql, explain, app_trace )
end
explain.scan(/Index Scan using (\w+)|Index Scan on (\w+)|Index Scan Backward using (\w+)/)
.each{|found| add_index_used(sql, explain, found.compact.first ) }
end
end
|
.config ⇒ Object
18
|
# File 'lib/testoscope.rb', line 18
def self.config; @@config ||= Config.new end
|
20
21
22
23
24
25
26
27
28
29
30
|
# File 'lib/testoscope.rb', line 20
def self.configure
yield(config) if block_given?
::ActiveRecord::Base.connection.class.include(AdapterUpgrade)
::ActiveRecord::Base.connection.execute( 'SET enable_seqscan=off;' ) if ::ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
config.tables.map!{|table| /.*[ "]#{table}[ "].*/ } if config.tables != :all
end
|
.get_all_indexes ⇒ Object
Alternative way to get index names “without” ActiveRecord “SELECT i.relname as indname FROM pg_index as idx JOIN pg_class as i ON i.oid = idx.indexrelid WHERE idx.indrelid::regclass = ANY( ARRAY::regclass[] )”)
.to_a.map(&:values).flatten
57
58
59
60
61
|
# File 'lib/testoscope.rb', line 57
def self.get_all_indexes
ActiveRecord::Base.connection.tables.map { |table|
[table, ActiveRecord::Base.connection.indexes(table).map(&:name)]
}.to_h
end
|
.print_results ⇒ Object
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
# File 'lib/testoscope.rb', line 63
def self.print_results
return yield(results) if block_given?
puts "\n<UNINTENDED BEHAVIOUR>\n" unless results[:unintended_behaviour].blank?
results[:unintended_behaviour].each do |sql, values|
puts "\nSQL:\n\n"
puts Niceql::Prettifier.prettify_sql( sql )
puts "\n\nAPP BACKTRACE:\n\n"
puts values[:backtrace].map{|arr| arr.join("\n")}.uniq.join("\n _____________________\n\n")
puts ''
puts values[:explain]
puts "\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
end
filtered_unused_indexes = []
get_all_indexes.each do |table, indexes|
unused_indexes = indexes - results[:indexes].keys
next if unused_indexes.blank? || !sql_has_analyzing_tables?(" #{table} ")
filtered_unused_indexes << "\n-----#{table}------\n"
filtered_unused_indexes << unused_indexes
end
if !filtered_unused_indexes.blank?
puts "\n<UNUSED INDEXES>\n"
puts filtered_unused_indexes.join("\n")
end
end
|
.results ⇒ Object
32
33
34
35
36
37
|
# File 'lib/testoscope.rb', line 32
def self.results
@results ||= {
unintended_behaviour: {},
indexes: {},
}
end
|
.sql_has_analyzing_tables?(sql) ⇒ Boolean
92
93
94
|
# File 'lib/testoscope.rb', line 92
def self.sql_has_analyzing_tables?(sql)
config.tables == :all || config.tables.any?{ |table| sql[table] }
end
|
.suspend_global_analyze(analyze) ⇒ Object
122
123
124
125
126
|
# File 'lib/testoscope.rb', line 122
def self.suspend_global_analyze( analyze )
was, config.analyze = config.analyze, analyze
yield
config.analyze = was
end
|