awesome_explain 0.3.0 → 1.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.
- checksums.yaml +5 -5
- data/.github/workflows/mongodb.yml +53 -0
- data/.github/workflows/postgres.yml +56 -0
- data/.gitignore +11 -0
- data/Appraisals +11 -0
- data/Gemfile.lock +209 -49
- data/LICENSE.txt +4 -20
- data/README.md +155 -7
- data/Rakefile +35 -1
- data/app/models/awesome_explain/application_record.rb +5 -0
- data/app/models/awesome_explain/controller.rb +20 -0
- data/app/models/awesome_explain/delayed_job.rb +7 -0
- data/app/models/awesome_explain/explain.rb +23 -0
- data/app/models/awesome_explain/log.rb +7 -0
- data/app/models/awesome_explain/pg_dml_stat.rb +4 -0
- data/app/models/awesome_explain/pg_seq_scan.rb +4 -0
- data/app/models/awesome_explain/plan_node.rb +52 -0
- data/app/models/awesome_explain/plan_tree.rb +66 -0
- data/app/models/awesome_explain/sidekiq_worker.rb +7 -0
- data/app/models/awesome_explain/sql_explain.rb +14 -0
- data/app/models/awesome_explain/sql_plan_node.rb +73 -0
- data/app/models/awesome_explain/sql_plan_stats.rb +34 -0
- data/app/models/awesome_explain/sql_plan_tree.rb +133 -0
- data/app/models/awesome_explain/sql_query.rb +7 -0
- data/app/models/awesome_explain/stacktrace.rb +11 -0
- data/awesome_explain.gemspec +16 -5
- data/bin/rails +14 -0
- data/data/mongodb/customers.bson +0 -0
- data/data/mongodb/customers.metadata.json +1 -0
- data/data/mongodb/line_items.bson +0 -0
- data/data/mongodb/line_items.metadata.json +1 -0
- data/data/mongodb/orders.bson +0 -0
- data/data/mongodb/orders.metadata.json +1 -0
- data/data/mongodb/products.bson +0 -0
- data/data/mongodb/products.metadata.json +1 -0
- data/data/postgresql/dvdrental.tar +0 -0
- data/db/migrate/20200507214801_stacktraces.rb +12 -0
- data/db/migrate/20200507214949_controllers.rb +16 -0
- data/db/migrate/20200507215205_logs.rb +22 -0
- data/db/migrate/20200507215243_explains.rb +27 -0
- data/gemfiles/rails_4.gemfile +7 -0
- data/gemfiles/rails_4.gemfile.lock +208 -0
- data/gemfiles/rails_5.gemfile +7 -0
- data/gemfiles/rails_5.gemfile.lock +209 -0
- data/gemfiles/rails_6.gemfile +7 -0
- data/gemfiles/rails_6.gemfile.lock +233 -0
- data/images/universe.png +0 -0
- data/lib/awesome_explain.rb +79 -2
- data/lib/awesome_explain/config.rb +196 -0
- data/lib/awesome_explain/engine.rb +5 -0
- data/lib/awesome_explain/insights/active_record_insights.rb +137 -0
- data/lib/awesome_explain/insights/base.rb +18 -0
- data/lib/awesome_explain/insights/mongoid_insights.rb +44 -0
- data/lib/awesome_explain/insights/sql_plans_insights.rb +64 -0
- data/lib/awesome_explain/kernel.rb +17 -0
- data/lib/awesome_explain/mongodb/base.rb +4 -0
- data/lib/awesome_explain/mongodb/command_start.rb +84 -0
- data/lib/awesome_explain/mongodb/command_success.rb +58 -0
- data/lib/awesome_explain/mongodb/formatter.rb +62 -0
- data/lib/awesome_explain/mongodb/helpers.rb +71 -0
- data/lib/awesome_explain/queue/command.rb +17 -0
- data/lib/awesome_explain/queue/simple_queue.rb +88 -0
- data/lib/awesome_explain/renderers/active_record.rb +114 -0
- data/lib/awesome_explain/renderers/base.rb +2 -0
- data/lib/awesome_explain/renderers/mongoid.rb +20 -33
- data/lib/awesome_explain/sidekiq_middleware.rb +17 -0
- data/lib/awesome_explain/stats/postgresql.rb +54 -0
- data/lib/awesome_explain/subscribers/active_record_passive_subscriber.rb +82 -0
- data/lib/awesome_explain/subscribers/active_record_subscriber.rb +187 -0
- data/lib/awesome_explain/subscribers/base.rb +3 -0
- data/lib/awesome_explain/subscribers/command_subscriber.rb +53 -0
- data/lib/awesome_explain/tasks/db.rb +325 -0
- data/lib/awesome_explain/utils/color.rb +16 -0
- data/lib/awesome_explain/version.rb +1 -1
- data/lib/tasks/ae.rake +28 -0
- data/lib/tasks/awesome_explain_tasks.rake +4 -0
- metadata +242 -25
- data/.travis.yml +0 -19
@@ -0,0 +1,88 @@
|
|
1
|
+
# module AwesomeExplain::Queue
|
2
|
+
# class SimpleQueue
|
3
|
+
# def initialize
|
4
|
+
# @elems = []
|
5
|
+
# @mutex = Mutex.new
|
6
|
+
# @cond_var = ConditionVariable.new
|
7
|
+
# end
|
8
|
+
|
9
|
+
# def <<(elem)
|
10
|
+
# @mutex.synchronize do
|
11
|
+
# @elems << elem
|
12
|
+
# @cond_var.signal
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
|
16
|
+
# def pop(blocking = true, timeout = nil)
|
17
|
+
# @mutex.synchronize do
|
18
|
+
# if blocking
|
19
|
+
# if timeout.nil?
|
20
|
+
# while @elems.empty?
|
21
|
+
# @cond_var.wait(@mutex)
|
22
|
+
# end
|
23
|
+
# else
|
24
|
+
# timeout_time = Time.now.to_f + timeout
|
25
|
+
# while @elems.empty? && (remaining_time = timeout_time - Time.now.to_f) > 0
|
26
|
+
# @cond_var.wait(@mutex, remaining_time)
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# raise ThreadError, 'queue empty' if @elems.empty?
|
31
|
+
# sleep 1
|
32
|
+
# @elems.shift
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
|
38
|
+
module AwesomeExplain::Queue
|
39
|
+
class SimpleQueue
|
40
|
+
include Singleton
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
@queue = Queue.new
|
44
|
+
# Thread.new do
|
45
|
+
# puts 'while true ==============================='
|
46
|
+
# command = @queue.pop(false)
|
47
|
+
# # command.run if command
|
48
|
+
# end
|
49
|
+
# @read_io, @write_io = IO.pipe
|
50
|
+
end
|
51
|
+
|
52
|
+
def <<(o)
|
53
|
+
# pop(false) until @queue.size < 2
|
54
|
+
if @queue.size >= 2
|
55
|
+
items = []
|
56
|
+
while @queue.size >= 2
|
57
|
+
items << @queue.pop
|
58
|
+
end
|
59
|
+
Thread.new { sleep 2; puts "Poped #{items}"; puts items.inspect }
|
60
|
+
end
|
61
|
+
puts "Adding to queue @@@@@@@@@@@@@"
|
62
|
+
@queue << o
|
63
|
+
# @write_io << '.'
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def pop(nonblock=false)
|
68
|
+
# return unless @queue.size >= 5
|
69
|
+
# @queue.size.times.each do
|
70
|
+
|
71
|
+
# end
|
72
|
+
puts Thread.current.inspect
|
73
|
+
o = @queue.pop(nonblock)
|
74
|
+
# @read_io.read(1)
|
75
|
+
puts "<------ Element poped ------>"
|
76
|
+
puts o
|
77
|
+
o
|
78
|
+
end
|
79
|
+
|
80
|
+
def size
|
81
|
+
@queue.size
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_io
|
85
|
+
@read_io
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module AwesomeExplain
|
2
|
+
module Renderers
|
3
|
+
class ActiveRecord
|
4
|
+
attr_reader :result, :query, :sql_explain
|
5
|
+
|
6
|
+
def initialize(query, result = nil)
|
7
|
+
@query = query
|
8
|
+
@result = result || explain_query
|
9
|
+
end
|
10
|
+
|
11
|
+
def explain_query
|
12
|
+
explain = AwesomeExplain::Config.instance.connection.raw_connection.exec(
|
13
|
+
"EXPLAIN (ANALYZE true, COSTS true, FORMAT json) #{query.to_sql}"
|
14
|
+
)
|
15
|
+
explain = explain.map { |h| h.values.first }.join("\n")
|
16
|
+
|
17
|
+
@sql_explain = SqlExplain.new(explain_output: explain)
|
18
|
+
end
|
19
|
+
|
20
|
+
def print
|
21
|
+
table = Terminal::Table.new do |t|
|
22
|
+
general_stats_section t
|
23
|
+
table_stats_section t
|
24
|
+
node_types_section t
|
25
|
+
index_stats_section t
|
26
|
+
end
|
27
|
+
puts table
|
28
|
+
end
|
29
|
+
|
30
|
+
def plan_stats
|
31
|
+
@plan_stats ||= @sql_explain.tree.plan_stats
|
32
|
+
end
|
33
|
+
|
34
|
+
def table_stats
|
35
|
+
@table_stats ||= plan_stats.table_stats
|
36
|
+
end
|
37
|
+
|
38
|
+
def node_type_stats
|
39
|
+
@node_type_stats ||= plan_stats.node_type_stats
|
40
|
+
end
|
41
|
+
|
42
|
+
def index_stats
|
43
|
+
@index_stats ||= plan_stats.index_stats
|
44
|
+
end
|
45
|
+
|
46
|
+
def seq_scans_row
|
47
|
+
color = plan_stats.seq_scans.positive? ? :cyan : :green
|
48
|
+
|
49
|
+
seq_scans_label = AwesomeExplain::Utils::Color.fg_color(
|
50
|
+
color,
|
51
|
+
'Seq Scans'
|
52
|
+
)
|
53
|
+
|
54
|
+
seq_scans_val = AwesomeExplain::Utils::Color.fg_color(
|
55
|
+
color,
|
56
|
+
plan_stats.seq_scans.to_s
|
57
|
+
)
|
58
|
+
|
59
|
+
[seq_scans_label, seq_scans_val]
|
60
|
+
end
|
61
|
+
|
62
|
+
def general_stats_section(t)
|
63
|
+
title = AwesomeExplain::Utils::Color.fg_color :yellow, 'General Stats'
|
64
|
+
t << [{ value: title, alignment: :center, colspan: 2}]
|
65
|
+
t << :separator
|
66
|
+
t << ['Table', 'Count']
|
67
|
+
t << :separator
|
68
|
+
t << ['Total Rows Planned', plan_stats.total_rows_planned]
|
69
|
+
t << ['Total Rows', plan_stats.total_rows]
|
70
|
+
t << ['Total Loops', plan_stats.total_loops]
|
71
|
+
t << seq_scans_row
|
72
|
+
t << ['Indexes Used', plan_stats.index_stats.size]
|
73
|
+
end
|
74
|
+
|
75
|
+
def table_stats_section(t)
|
76
|
+
title = AwesomeExplain::Utils::Color.fg_color :yellow, 'Table Stats'
|
77
|
+
t << :separator
|
78
|
+
t << [{ value: title, alignment: :center, colspan: 2}]
|
79
|
+
t << :separator
|
80
|
+
t << ['Table', 'Count']
|
81
|
+
t << :separator
|
82
|
+
table_stats.each do |table_name, stats|
|
83
|
+
t << [table_name, stats.dig(:count)]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def node_types_section(t)
|
88
|
+
title = AwesomeExplain::Utils::Color.fg_color :yellow, 'Node Type Stats'
|
89
|
+
t << :separator
|
90
|
+
t << [{ value: title, alignment: :center, colspan: 2}]
|
91
|
+
t << :separator
|
92
|
+
t << ['Node Type', 'Count']
|
93
|
+
t << :separator
|
94
|
+
node_type_stats.each do |node_type, stats|
|
95
|
+
t << [node_type, stats.dig(:count)]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def index_stats_section(t)
|
100
|
+
if index_stats.size.positive?
|
101
|
+
title = AwesomeExplain::Utils::Color.fg_color :yellow, 'Index Stats'
|
102
|
+
t << :separator
|
103
|
+
t << [{ value: title, alignment: :center, colspan: 2}]
|
104
|
+
t << :separator
|
105
|
+
t << ['Index Name', 'Count']
|
106
|
+
t << :separator
|
107
|
+
index_stats.each do |index, stats|
|
108
|
+
t << [index, stats.dig(:count)]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -3,41 +3,25 @@ module AwesomeExplain
|
|
3
3
|
class Mongoid
|
4
4
|
attr_reader :result, :query
|
5
5
|
|
6
|
-
|
7
|
-
none: 0, bright: 1, black: 30,
|
8
|
-
red: 31, green: 32, yellow: 33,
|
9
|
-
blue: 34, magenta: 35, cyan: 36,
|
10
|
-
white: 37, default: 39
|
11
|
-
}
|
12
|
-
|
13
|
-
def initialize(query)
|
6
|
+
def initialize(query, result = nil)
|
14
7
|
@query = query
|
8
|
+
@result = result || query.explain
|
15
9
|
end
|
16
10
|
|
17
11
|
def print
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
# Text foreground color
|
24
|
-
def fg_color(clr, text = nil)
|
25
|
-
"\x1B[" + (COLOR_ESCAPES[clr] || 0).to_s + 'm' + (text ? text + "\x1B[0m" : '')
|
26
|
-
end
|
27
|
-
|
28
|
-
# Text background color
|
29
|
-
def bg_color(clr, text = nil)
|
30
|
-
"\x1B[" + ((COLOR_ESCAPES[clr] || 0) + 10).to_s + 'm' + (text ? text + "\x1B[0m" : '')
|
12
|
+
ap result, indent: -2
|
13
|
+
puts
|
14
|
+
puts explain_summary
|
15
|
+
puts
|
31
16
|
end
|
32
17
|
|
33
|
-
def
|
34
|
-
ap result, indent: -2
|
18
|
+
def explain_summary
|
35
19
|
table = Terminal::Table.new do |t|
|
36
20
|
winning_plan_label = 'Winning Plan'
|
37
21
|
plan_data = winning_plan_data
|
38
22
|
winning_plan_str = plan_data[0]
|
39
23
|
used_indexes = plan_data[1]
|
40
|
-
winning_plan_label = fg_color :red, winning_plan_label if winning_plan_str =~ /COLLSCAN/
|
24
|
+
winning_plan_label = AwesomeExplain::Utils::Color.fg_color :red, winning_plan_label if winning_plan_str =~ /COLLSCAN/
|
41
25
|
t << [winning_plan_label, winning_plan_str]
|
42
26
|
t << :separator
|
43
27
|
t << ['Used Indexes', used_indexes.join(', ')]
|
@@ -59,8 +43,8 @@ module AwesomeExplain
|
|
59
43
|
exec_label_ms = 'Execution time(ms)'
|
60
44
|
|
61
45
|
if exec_time > 10
|
62
|
-
exec_label = fg_color :red, exec_label
|
63
|
-
exec_label_ms = fg_color :red, exec_label_ms
|
46
|
+
exec_label = AwesomeExplain::Utils::Color.fg_color :red, exec_label
|
47
|
+
exec_label_ms = AwesomeExplain::Utils::Color.fg_color :red, exec_label_ms
|
64
48
|
end
|
65
49
|
t << [exec_label_ms, exec_time_ms]
|
66
50
|
t << :separator
|
@@ -68,16 +52,18 @@ module AwesomeExplain
|
|
68
52
|
end
|
69
53
|
end
|
70
54
|
|
71
|
-
|
72
|
-
|
73
|
-
|
55
|
+
table
|
56
|
+
end
|
57
|
+
|
58
|
+
def parsed_query
|
59
|
+
root.dig('parsedQuery')
|
74
60
|
end
|
75
61
|
|
76
62
|
def winning_plan_data
|
77
63
|
used_indexes = []
|
78
64
|
plan = winning_plan
|
79
65
|
plan_str = stage_label_and_stats(plan)
|
80
|
-
plan_str = dig_input_stages(plan.dig('inputStage'), plan_str, used_indexes) if plan
|
66
|
+
plan_str = dig_input_stages(plan.dig('inputStage'), plan_str, used_indexes) if plan&.dig('inputStage')
|
81
67
|
|
82
68
|
[plan_str, used_indexes]
|
83
69
|
end
|
@@ -87,15 +73,15 @@ module AwesomeExplain
|
|
87
73
|
end
|
88
74
|
|
89
75
|
def winning_plan
|
90
|
-
root.dig('executionStats', 'executionStages') || root.dig('queryPlanner', 'winningPlan')
|
76
|
+
root.dig('executionStats', 'executionStages') || root.dig('queryPlanner', 'winningPlan') || root['stages'].first['$cursor'].dig('queryPlanner', 'winningPlan') || {}
|
91
77
|
end
|
92
78
|
|
93
79
|
def rejected_plans
|
94
|
-
root.dig('queryPlanner', 'rejectedPlans')
|
80
|
+
root.dig('queryPlanner', 'rejectedPlans') || root['stages']&.first['$cursor'].dig('queryPlanner', 'rejectedPlans') || {}
|
95
81
|
end
|
96
82
|
|
97
83
|
def execution_stats
|
98
|
-
root.dig('executionStats')
|
84
|
+
root.dig('executionStats') || root['stages']&.first&.dig('$cursor')&.dig('executionStats') || {}
|
99
85
|
end
|
100
86
|
|
101
87
|
def dig_input_stages(stage, str, used_indexes, input_stages = false)
|
@@ -123,6 +109,7 @@ module AwesomeExplain
|
|
123
109
|
end
|
124
110
|
|
125
111
|
def stage_label_and_stats(stage)
|
112
|
+
return unless stage.present?
|
126
113
|
str = "#{stage.dig('stage')} ("
|
127
114
|
str += "#{stage.dig('docsExamined')} / " if stage.dig('docsExamined').present?
|
128
115
|
str += stage.dig('nReturned').to_s if stage.dig('nReturned').present?
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module AwesomeExplain
|
2
|
+
class SidekiqMiddleware
|
3
|
+
# def call(worker_class, job, queue, redis_pool)
|
4
|
+
def call(worker_class, job, queue)
|
5
|
+
begin
|
6
|
+
Thread.current[:sidekiq_worker_class] = worker_class.class.name
|
7
|
+
Thread.current[:sidekiq_job] = job
|
8
|
+
Thread.current[:sidekiq_queue] = queue
|
9
|
+
Thread.current['ae_source'] = 'sidekiq'
|
10
|
+
rescue => exception
|
11
|
+
# Do nothing
|
12
|
+
end
|
13
|
+
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module AwesomeExplain
|
2
|
+
module Stats
|
3
|
+
class PostgreSQL
|
4
|
+
def self.upsert!
|
5
|
+
upsert_seq_scans!
|
6
|
+
upsert_dml_stats!
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.upsert_seq_scans!
|
10
|
+
result = ActiveRecord::Base.connection.execute(seq_scans_sql).to_a
|
11
|
+
result.each do |row|
|
12
|
+
row = OpenStruct.new(row)
|
13
|
+
pg_seq_scan = AwesomeExplain::PgSeqScan.find_or_create_by({
|
14
|
+
schema_name: row.schema_name,
|
15
|
+
table_name: row.table_name
|
16
|
+
})
|
17
|
+
|
18
|
+
pg_seq_scan.update({
|
19
|
+
seq_scan: row.seq_scan,
|
20
|
+
seq_tup_read: row.seq_tup_read,
|
21
|
+
idx_scan: row.idx_scan,
|
22
|
+
idx_tup_fetch: row.idx_tup_fetch,
|
23
|
+
size_bytes: row.size_bytes
|
24
|
+
})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.upsert_dml_stats!
|
29
|
+
result = ActiveRecord::Base.connection.execute(dml_stats_sql).to_a
|
30
|
+
result.each do |row|
|
31
|
+
row = OpenStruct.new(row)
|
32
|
+
pg_dml_stat = AwesomeExplain::PgDmlStat.find_or_create_by({
|
33
|
+
schema_name: row.schema_name,
|
34
|
+
table_name: row.table_name
|
35
|
+
})
|
36
|
+
|
37
|
+
pg_dml_stat.update({
|
38
|
+
total_inserts: row.n_tup_ins,
|
39
|
+
total_updates: row.n_tup_upd,
|
40
|
+
total_deletes: row.n_tup_del,
|
41
|
+
})
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.seq_scans_sql
|
46
|
+
"select schemaname as schema_name, relname as table_name, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch, pg_relation_size(schemaname::text || '.'::text || relname::text) as size_bytes from pg_stat_user_tables"
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.dml_stats_sql
|
50
|
+
"SELECT schemaname as schema_name, relname as table_name, n_tup_ins, n_tup_upd, n_tup_del FROM pg_stat_user_tables"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module AwesomeExplain::Subscribers
|
2
|
+
class ActiveRecordPassiveSubscriber < ActiveSupport::LogSubscriber
|
3
|
+
def sql(event)
|
4
|
+
if track_sql(event)
|
5
|
+
sql = event.payload[:sql]
|
6
|
+
begin
|
7
|
+
table_name_and_schema = extract_table_name_and_schema(sql)
|
8
|
+
table_name = table_name_and_schema.first
|
9
|
+
schema_name = table_name_and_schema.last
|
10
|
+
request_id = event.payload[:connection_id]
|
11
|
+
binds = event.payload[:binds]
|
12
|
+
cached = event.payload[:name] == 'CACHE'
|
13
|
+
operation = extract_sql_operation(sql)
|
14
|
+
name = event.payload[:name]
|
15
|
+
|
16
|
+
connection_id = event.payload[:connection_id]
|
17
|
+
connection = ::AwesomeExplain::Config.instance.connection
|
18
|
+
explain_uuid = SecureRandom.uuid
|
19
|
+
explain = connection.raw_connection.exec("EXPLAIN (ANALYZE true, COSTS true, FORMAT json) #{sql}")
|
20
|
+
explain = explain.map { |h| h.values.first }.join("\n")
|
21
|
+
explain = ::AwesomeExplain::SqlExplain.new(explain_output: explain)
|
22
|
+
AwesomeExplain::Insights::SqlPlansInsights.add explain.tree.plan_stats
|
23
|
+
AwesomeExplain::Insights::SqlPlansInsights.add_query sql
|
24
|
+
rescue => exception
|
25
|
+
logger.warn sql
|
26
|
+
logger.warn exception.to_s
|
27
|
+
logger.warn exception.backtrace[0..5]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def track_sql(event)
|
33
|
+
return false unless Thread.current['ae_analyze']
|
34
|
+
return false if event.payload[:connection].class.name == 'ActiveRecord::ConnectionAdapters::SQLite3Adapter'
|
35
|
+
sql = event.payload[:sql]
|
36
|
+
!sql.match(/EXPLAIN|SAVEPOINT|nextval|CREATE|BEGIN|COMMIT|ROLLBACK|begin|commit|rollback|ar_|sql_|pg_|explain|logs|controllers|stacktraces|schema_migrations|delayed_jobs/) &&
|
37
|
+
sql.strip == sql &&
|
38
|
+
event.payload[:name] != 'SCHEMA'
|
39
|
+
end
|
40
|
+
|
41
|
+
def ddm_query?(sql)
|
42
|
+
matched = sql.match(/INSERT|DELETE|UPDATE/)
|
43
|
+
matched.present? && matched[0].present?
|
44
|
+
end
|
45
|
+
|
46
|
+
def resolve_source_name
|
47
|
+
Thread.current['ae_source'] || DEFAULT_SOURCE_NAME
|
48
|
+
end
|
49
|
+
|
50
|
+
def db_logging_enbled?
|
51
|
+
# return true if Thread.current['ae_analyze']
|
52
|
+
return false if Rails.const_defined?('Console')
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def extract_sql_operation(sql)
|
57
|
+
sql.match(/SELECT|INSERT|DELETE|UPDATE/)[0]
|
58
|
+
end
|
59
|
+
|
60
|
+
def extract_table_name_and_schema(sql)
|
61
|
+
matched = sql.match(/FROM\s+(\"\w+\")\.?(\"\w+\")?/)
|
62
|
+
return reduce_table_and_schema(matched) if matched && matched[1].present?
|
63
|
+
|
64
|
+
matched = sql.match(/INSERT INTO\s+(\"\w+\")\.?(\"\w+\")?/)
|
65
|
+
return reduce_table_and_schema(matched) if matched && matched[1].present?
|
66
|
+
|
67
|
+
matched = sql.match(/UPDATE\s+(\"\w+\")\.?(\"\w+\")?/)
|
68
|
+
return reduce_table_and_schema(matched) if matched && matched[1].present?
|
69
|
+
end
|
70
|
+
|
71
|
+
def reduce_table_and_schema(matched)
|
72
|
+
if matched[1].present?
|
73
|
+
table_name = matched[2].nil? ? matched[1] : matched[2]
|
74
|
+
table_name = table_name.gsub(/\"/, '')
|
75
|
+
schema_name = matched[2].nil? ? 'public' : matched[1]
|
76
|
+
schema_name = schema_name.gsub(/\"/, '')
|
77
|
+
|
78
|
+
return [table_name, schema_name]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|