Module: TingYun::Agent::Database
- Extended by:
- Database
- Included in:
- Database
- Defined in:
- lib/ting_yun/agent/database.rb
Defined Under Namespace
Classes: ConnectionManager, Obfuscator, Statement
Constant Summary
collapse
- MAX_QUERY_LENGTH =
16384
- RECORD_FOR =
[:raw, :obfuscated].freeze
- SUPPORTED_ADAPTERS_FOR_EXPLAIN =
%w[postgres postgresql mysql2 mysql sqlite].freeze
- QUERY_PLAN =
'QUERY PLAN'.freeze
- SQLITE_EXPLAIN_COLUMNS =
%w[addr opcode p1 p2 p3 p4 p5 comment]
- KNOWN_OPERATIONS =
[
'alter',
'select',
'update',
'delete',
'insert',
'create',
'show',
'set',
'exec',
'execute',
'call'
]
Regexp.new('/\*.*?\*/', Regexp::MULTILINE).freeze
- EMPTY_STRING =
''.freeze
Instance Method Summary
collapse
Instance Method Details
#adapter_from_config(config) ⇒ Object
89
90
91
92
93
94
95
96
97
|
# File 'lib/ting_yun/agent/database.rb', line 89
def adapter_from_config(config)
if config[:adapter]
return config[:adapter].to_s
elsif config[:uri] && config[:uri].to_s =~ /^jdbc:([^:]+):/
return $1
end
end
|
#capture_query(query) ⇒ Object
#close_connections ⇒ Object
226
227
228
|
# File 'lib/ting_yun/agent/database.rb', line 226
def close_connections
ConnectionManager.instance.close_connections
end
|
#explain(sql, config, explainer = nil) ⇒ 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
|
# File 'lib/ting_yun/agent/database.rb', line 63
def explain(sql, config, explainer=nil)
return unless explainer && is_select?(sql)
if sql[-3,3] == '...'
TingYun::Agent.logger.debug('Unable to collect explain plan for truncated query.')
return
end
if parameterized?(sql)
TingYun::Agent.logger.debug('Unable to collect explain plan for parameterized query.')
return
end
adapter = adapter_from_config(config)
if !SUPPORTED_ADAPTERS_FOR_EXPLAIN.include?(adapter)
TingYun::Agent.logger.debug("Not collecting explain plan because an unknown connection adapter ('#{adapter}') was used.")
return
end
handle_exception_in_explain do
plan = explainer.call(config, sql)
return process_resultset(plan, adapter) if plan
end
end
|
#explain_sql(sql, config, explainer = nil) ⇒ Object
54
55
56
57
58
59
|
# File 'lib/ting_yun/agent/database.rb', line 54
def explain_sql(sql, config, explainer=nil)
return nil unless sql && explainer && config
_sql = sql.split(";\n")[0] explain_plan = explain(_sql, config, explainer)
return explain_plan || {"dialect"=> nil, "keys"=>[], "values"=>[]}
end
|
#get_connection(config, &connector) ⇒ Object
222
223
224
|
# File 'lib/ting_yun/agent/database.rb', line 222
def get_connection(config, &connector)
ConnectionManager.instance.get_connection(config, &connector)
end
|
#handle_exception_in_explain ⇒ Object
214
215
216
217
218
219
|
# File 'lib/ting_yun/agent/database.rb', line 214
def handle_exception_in_explain
yield
rescue => e
::TingYun::Agent.logger.error("Error getting query plan:", e)
nil
end
|
#is_select?(sql) ⇒ Boolean
104
105
106
|
# File 'lib/ting_yun/agent/database.rb', line 104
def is_select?(sql)
parse_operation_from_query(sql) == 'select'
end
|
#obfuscate_sql(sql) ⇒ Object
13
14
15
|
# File 'lib/ting_yun/agent/database.rb', line 13
def obfuscate_sql(sql)
Obfuscator.instance.obfuscator.call(sql)
end
|
#parameterized?(sql) ⇒ Boolean
100
101
102
|
# File 'lib/ting_yun/agent/database.rb', line 100
def parameterized?(sql)
Obfuscator.instance.obfuscate_single_quote_literals(sql) =~ /\$\d+/
end
|
#parse_operation_from_query(sql) ⇒ Object
#process_explain_results_mysql(results) ⇒ Object
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
# File 'lib/ting_yun/agent/database.rb', line 152
def process_explain_results_mysql(results)
return string_explain_plan_results("MySQL", results) if results.is_a?(String)
= []
values = []
if results.is_a?(Array)
= results.first.keys
results.each do |row|
values << .map { |h| row[h] }
end
else
results.each_hash do |row|
= row.keys
values << .map { |h| row[h] }
end
end
{"dialect"=> "MySQL", "keys"=>, "values"=>values}
end
|
#process_explain_results_mysql2(results) ⇒ Object
144
145
146
147
148
149
150
|
# File 'lib/ting_yun/agent/database.rb', line 144
def process_explain_results_mysql2(results)
return string_explain_plan_results("MySQL", results) if results.is_a?(String)
= results.fields
values = []
results.each { |row| values << row }
{"dialect"=> "MySQL", "keys"=>, "values"=>values}
end
|
#process_explain_results_postgres(results) ⇒ Object
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
# File 'lib/ting_yun/agent/database.rb', line 123
def process_explain_results_postgres(results)
if results.is_a?(String)
query_plan_string = results
else
lines = []
results.each { |row| lines << row[QUERY_PLAN] }
query_plan_string = lines.join("\n")
end
unless record_sql_method("nbs.action_tracer.record_sql") == :raw
query_plan_string = Obfuscator.instance.obfuscate_postgres_explain(query_plan_string)
end
values = query_plan_string.split("\n").map { |line| [line] }
{"dialect"=> "PostgreSQL", "keys"=>[QUERY_PLAN], "values"=>values}
end
|
#process_explain_results_sqlite(results) ⇒ Object
176
177
178
179
180
181
182
183
184
|
# File 'lib/ting_yun/agent/database.rb', line 176
def process_explain_results_sqlite(results)
return string_explain_plan_results("sqlite", results) if results.is_a?(String)
= SQLITE_EXPLAIN_COLUMNS
values = []
results.each do |row|
values << .map { |h| row[h] }
end
{"dialect"=> "sqlite", "keys"=>, "values"=>values}
end
|
#process_resultset(results, adapter) ⇒ Object
108
109
110
111
112
113
114
115
116
117
118
119
|
# File 'lib/ting_yun/agent/database.rb', line 108
def process_resultset(results ,adapter)
case adapter.to_s
when 'postgres', 'postgresql'
process_explain_results_postgres(results)
when 'mysql2'
process_explain_results_mysql2(results)
when 'mysql'
process_explain_results_mysql(results)
when 'sqlite'
process_explain_results_sqlite(results)
end
end
|
#record_sql_method(key) ⇒ Object
37
38
39
40
41
42
43
44
45
46
47
|
# File 'lib/ting_yun/agent/database.rb', line 37
def record_sql_method(key)
case Agent.config[key].to_s
when 'off'
:off
when 'raw'
:raw
else
:obfuscated
end
end
|
#should_action_collect_explain_plans? ⇒ Boolean
49
50
51
52
|
# File 'lib/ting_yun/agent/database.rb', line 49
def should_action_collect_explain_plans?
should_record_sql?("nbs.action_tracer.record_sql") &&
Agent.config["nbs.action_tracer.explain_enabled".to_sym]
end
|
#should_record_sql?(key) ⇒ Boolean
33
34
35
|
# File 'lib/ting_yun/agent/database.rb', line 33
def should_record_sql?(key)
RECORD_FOR.include?(record_sql_method(key.to_sym))
end
|
#string_explain_plan_results(adpater, results) ⇒ Object
140
141
142
|
# File 'lib/ting_yun/agent/database.rb', line 140
def string_explain_plan_results(adpater, results)
{"dialect"=> adpater, "keys"=>[], "values"=>[results]}
end
|
#truncate_query(query) ⇒ Object
22
23
24
25
26
27
28
|
# File 'lib/ting_yun/agent/database.rb', line 22
def truncate_query(query)
if query.length > (MAX_QUERY_LENGTH - 4)
query[0..MAX_QUERY_LENGTH - 4] + '...'
else
query
end
end
|