newrelic_rpm 3.15.1.316 → 3.15.2.317
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +41 -0
- data/lib/new_relic/agent/agent.rb +1 -1
- data/lib/new_relic/agent/autostart.rb +1 -1
- data/lib/new_relic/agent/database.rb +72 -165
- data/lib/new_relic/agent/database/explain_plan_helpers.rb +127 -0
- data/lib/new_relic/agent/database/obfuscation_helpers.rb +2 -1
- data/lib/new_relic/agent/database/postgres_explain_obfuscator.rb +0 -2
- data/lib/new_relic/agent/instrumentation/active_record.rb +5 -5
- data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +10 -8
- data/lib/new_relic/agent/instrumentation/data_mapper.rb +2 -1
- data/lib/new_relic/agent/instrumentation/grape.rb +23 -9
- data/lib/new_relic/agent/instrumentation/mongodb_command_subscriber.rb +1 -6
- data/lib/new_relic/agent/instrumentation/padrino.rb +36 -0
- data/lib/new_relic/agent/method_tracer.rb +20 -18
- data/lib/new_relic/agent/new_relic_service.rb +3 -33
- data/lib/new_relic/agent/pipe_service.rb +0 -4
- data/lib/new_relic/agent/sql_sampler.rb +3 -3
- data/lib/new_relic/agent/transaction/trace_node.rb +2 -5
- data/lib/new_relic/agent/transaction_sampler.rb +2 -2
- data/lib/new_relic/metric_data.rb +6 -14
- data/lib/new_relic/version.rb +1 -1
- data/newrelic_rpm.gemspec +1 -1
- data/test/environments/lib/environments/runner.rb +1 -1
- data/test/environments/rails40/Gemfile +9 -0
- data/test/environments/rails41/Gemfile +9 -0
- data/test/environments/rails42/Gemfile +9 -0
- data/test/multiverse/lib/multiverse/suite.rb +12 -0
- data/test/multiverse/suites/agent_only/harvest_timestamps_test.rb +0 -31
- data/test/multiverse/suites/grape/Envfile +1 -1
- data/test/multiverse/suites/mongo/Envfile +3 -0
- data/test/multiverse/suites/mongo/mongo2_instrumentation_test.rb +3 -2
- data/test/multiverse/suites/padrino/Envfile +8 -0
- data/test/multiverse/suites/typhoeus/Envfile +21 -0
- data/test/new_relic/agent/database_test.rb +216 -144
- data/test/new_relic/agent/new_relic_service_test.rb +0 -58
- data/test/new_relic/metric_data_test.rb +24 -71
- data/test/new_relic/metric_spec_test.rb +3 -4
- data/test/new_relic/rack/developer_mode_test.rb +5 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e8eefedf383e69a41a5dfb8f9c607a8c93f3390
|
4
|
+
data.tar.gz: 4aa588cdb66a876439bc666ab40a5dd8bb829a50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa28051c2fe6daef0a6adbbb71cb884da9ecb83ec26835ad6e5de7ddbbfd43180bdd220669ca226dbba226668b7f9e1c56274ecc01e0fe3dd9af7a0db901f9da
|
7
|
+
data.tar.gz: 6e9e0e4b9183378f59fab486acf5ca1eeae547f0850f6657ba50cf3df7122a9a665bfb5ce4207439c345775286516ac7c48baf3958a9182715cbe68c6ff490a4
|
data/CHANGELOG
CHANGED
@@ -1,6 +1,47 @@
|
|
1
1
|
# New Relic Ruby Agent Release Notes #
|
2
2
|
|
3
|
+
## v3.15.2 ##
|
4
|
+
|
5
|
+
* Run explain plans on parameterized slow queries in AR4
|
6
|
+
|
7
|
+
In our ActiveRecord 4 instrumentation, we moved to tracing slow queries
|
8
|
+
using the payloads from ActiveSupport::Notifications `sql.active_record`
|
9
|
+
events. As a result, we were unable to run explain plans on parameterized
|
10
|
+
queries. This has now been updated to pass along and use the parameter values,
|
11
|
+
when available, to get the explain plans.
|
12
|
+
|
13
|
+
* Fix getMore metric grouping issue in Mongo 2.2.x instrumentation
|
14
|
+
|
15
|
+
A metric grouping issue had cropped up when using the most recent Mongo gem
|
16
|
+
(2.2.0) with the most recent release of the server (3.2.4). We now have a more
|
17
|
+
future-proof setup for preventing these.
|
18
|
+
|
19
|
+
* Restore older DataMapper support after password obfuscation fix
|
20
|
+
|
21
|
+
Back in 3.14.3, we released a fix to avoid inadvertently sending sensitive
|
22
|
+
information from DataMapper SQLErrors. Our implementation did not account for
|
23
|
+
DataMapper versions below v0.10.0 not implementing the #options accessor.
|
24
|
+
Thanks Bram de Vries for the fix to our fix!
|
25
|
+
|
26
|
+
* Padrino 0.13.1 Support
|
27
|
+
|
28
|
+
Users with Padrino 0.13.x apps were previously seeing the default transaction
|
29
|
+
name "(unknown)" for all of their routes. We now provide the full Padrino
|
30
|
+
route template in transaction names, including any parameter placeholders.
|
31
|
+
Thanks Robert Schulze for the contribution!
|
32
|
+
|
33
|
+
* Update transaction naming for Grape 0.16.x
|
34
|
+
|
35
|
+
In Grape 0.16.x, `route_` methods are no longer prefixed. Thanks to Daniel
|
36
|
+
Doubrovkine for the contribution!
|
37
|
+
|
38
|
+
* Fix name collision on method created for default metric name fix
|
39
|
+
|
40
|
+
We had a name collision with the yard gem, which sets a `class_name` method
|
41
|
+
on Module. We've renamed our internal method to `derived_class_name` instead.
|
42
|
+
|
3
43
|
## v3.15.1 ##
|
44
|
+
|
4
45
|
* Rack 2 alpha support
|
5
46
|
|
6
47
|
This release includes experimental support for Rack 2 as of 2.0.0.alpha.
|
@@ -1033,7 +1033,7 @@ module NewRelic
|
|
1033
1033
|
# drop any stored data and reset to a clean state.
|
1034
1034
|
#
|
1035
1035
|
# #merge!(payload)
|
1036
|
-
# merge the given
|
1036
|
+
# merge the given payload back into the internal buffer of the
|
1037
1037
|
# container, so that it may be harvested again later.
|
1038
1038
|
#
|
1039
1039
|
def harvest_and_send_from_container(container, endpoint)
|
@@ -8,7 +8,7 @@ module NewRelic
|
|
8
8
|
# monitoring.
|
9
9
|
#
|
10
10
|
# If the agent is in a monitored environment (e.g. production) it will
|
11
|
-
# attempt to avoid starting at "
|
11
|
+
# attempt to avoid starting at "inappropriate" times, for example in an IRB
|
12
12
|
# session. On Heroku, logs typically go to STDOUT so agent logs can spam
|
13
13
|
# the console during interactive sessions.
|
14
14
|
#
|
@@ -3,9 +3,8 @@
|
|
3
3
|
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
4
|
|
5
5
|
require 'singleton'
|
6
|
-
require 'new_relic/agent/database/
|
6
|
+
require 'new_relic/agent/database/explain_plan_helpers'
|
7
7
|
require 'new_relic/agent/database/obfuscator'
|
8
|
-
require 'new_relic/agent/database/postgres_explain_obfuscator'
|
9
8
|
|
10
9
|
module NewRelic
|
11
10
|
# columns for a mysql explain plan
|
@@ -95,157 +94,18 @@ module NewRelic
|
|
95
94
|
ConnectionManager.instance.close_connections
|
96
95
|
end
|
97
96
|
|
98
|
-
# This takes a connection config hash from ActiveRecord or Sequel and
|
99
|
-
# returns a string describing the associated database adapter
|
100
|
-
def adapter_from_config(config)
|
101
|
-
if config[:adapter]
|
102
|
-
return config[:adapter].to_s
|
103
|
-
elsif config[:uri] && config[:uri].to_s =~ /^jdbc:([^:]+):/
|
104
|
-
# This case is for Sequel with the jdbc-mysql, jdbc-postgres, or
|
105
|
-
# jdbc-sqlite3 gems.
|
106
|
-
return $1
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
97
|
# Perform this in the runtime environment of a managed
|
111
98
|
# application, to explain the sql statement executed within a
|
112
|
-
# node of a transaction sample. Returns an array of
|
113
|
-
#
|
114
|
-
# strings for each column returned by the
|
115
|
-
# Note this happens only for statements whose execution time
|
116
|
-
#
|
117
|
-
#
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
statement
|
122
|
-
explain_plan = explain_statement(statement, connection_config, explainer)
|
123
|
-
return explain_plan || []
|
124
|
-
end
|
125
|
-
|
126
|
-
SUPPORTED_ADAPTERS_FOR_EXPLAIN = %w[postgres postgresql mysql2 mysql sqlite].freeze
|
127
|
-
|
128
|
-
def explain_statement(statement, config, explainer)
|
129
|
-
return unless explainer && is_select?(statement)
|
130
|
-
|
131
|
-
if statement[-3,3] == '...'
|
132
|
-
NewRelic::Agent.logger.debug('Unable to collect explain plan for truncated query.')
|
133
|
-
return
|
134
|
-
end
|
135
|
-
|
136
|
-
if parameterized?(statement)
|
137
|
-
NewRelic::Agent.logger.debug('Unable to collect explain plan for parameterized query.')
|
138
|
-
return
|
139
|
-
end
|
140
|
-
|
141
|
-
adapter = adapter_from_config(config)
|
142
|
-
if !SUPPORTED_ADAPTERS_FOR_EXPLAIN.include?(adapter)
|
143
|
-
NewRelic::Agent.logger.debug("Not collecting explain plan because an unknown connection adapter ('#{adapter}') was used.")
|
144
|
-
return
|
145
|
-
end
|
146
|
-
|
147
|
-
handle_exception_in_explain do
|
148
|
-
start = Time.now
|
149
|
-
plan = explainer.call(config, statement)
|
150
|
-
::NewRelic::Agent.record_metric("Supportability/Database/execute_explain_plan", Time.now - start)
|
151
|
-
return process_resultset(plan, adapter) if plan
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def process_resultset(results, adapter)
|
156
|
-
case adapter.to_s
|
157
|
-
when 'postgres', 'postgresql'
|
158
|
-
process_explain_results_postgres(results)
|
159
|
-
when 'mysql2'
|
160
|
-
process_explain_results_mysql2(results)
|
161
|
-
when 'mysql'
|
162
|
-
process_explain_results_mysql(results)
|
163
|
-
when 'sqlite'
|
164
|
-
process_explain_results_sqlite(results)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
QUERY_PLAN = 'QUERY PLAN'.freeze
|
169
|
-
|
170
|
-
def process_explain_results_postgres(results)
|
171
|
-
if results.is_a?(String)
|
172
|
-
query_plan_string = results
|
173
|
-
else
|
174
|
-
lines = []
|
175
|
-
results.each { |row| lines << row[QUERY_PLAN] }
|
176
|
-
query_plan_string = lines.join("\n")
|
177
|
-
end
|
178
|
-
|
179
|
-
unless record_sql_method == :raw
|
180
|
-
query_plan_string = NewRelic::Agent::Database::PostgresExplainObfuscator.obfuscate(query_plan_string)
|
181
|
-
end
|
182
|
-
values = query_plan_string.split("\n").map { |line| [line] }
|
183
|
-
|
184
|
-
[[QUERY_PLAN], values]
|
185
|
-
end
|
186
|
-
|
187
|
-
# Sequel returns explain plans as just one big pre-formatted String
|
188
|
-
# In that case, we send a nil headers array, and the single string
|
189
|
-
# wrapped in an array for the values.
|
190
|
-
# Note that we don't use this method for Postgres explain plans, since
|
191
|
-
# they need to be passed through the explain plan obfuscator first.
|
192
|
-
def string_explain_plan_results(results)
|
193
|
-
[nil, [results]]
|
194
|
-
end
|
195
|
-
|
196
|
-
def process_explain_results_mysql(results)
|
197
|
-
return string_explain_plan_results(results) if results.is_a?(String)
|
198
|
-
headers = []
|
199
|
-
values = []
|
200
|
-
if results.is_a?(Array)
|
201
|
-
# We're probably using the jdbc-mysql gem for JRuby, which will give
|
202
|
-
# us an array of hashes.
|
203
|
-
headers = results.first.keys
|
204
|
-
results.each do |row|
|
205
|
-
values << headers.map { |h| row[h] }
|
206
|
-
end
|
207
|
-
else
|
208
|
-
# We're probably using the native mysql driver gem, which will give us
|
209
|
-
# a Mysql::Result object that responds to each_hash
|
210
|
-
results.each_hash do |row|
|
211
|
-
headers = row.keys
|
212
|
-
values << headers.map { |h| row[h] }
|
213
|
-
end
|
214
|
-
end
|
215
|
-
[headers, values]
|
216
|
-
end
|
217
|
-
|
218
|
-
def process_explain_results_mysql2(results)
|
219
|
-
return string_explain_plan_results(results) if results.is_a?(String)
|
220
|
-
headers = results.fields
|
221
|
-
values = []
|
222
|
-
results.each { |row| values << row }
|
223
|
-
[headers, values]
|
224
|
-
end
|
225
|
-
|
226
|
-
SQLITE_EXPLAIN_COLUMNS = %w[addr opcode p1 p2 p3 p4 p5 comment]
|
227
|
-
|
228
|
-
def process_explain_results_sqlite(results)
|
229
|
-
return string_explain_plan_results(results) if results.is_a?(String)
|
230
|
-
headers = SQLITE_EXPLAIN_COLUMNS
|
231
|
-
values = []
|
232
|
-
results.each do |row|
|
233
|
-
values << headers.map { |h| row[h] }
|
234
|
-
end
|
235
|
-
[headers, values]
|
236
|
-
end
|
237
|
-
|
238
|
-
def handle_exception_in_explain
|
239
|
-
yield
|
240
|
-
rescue => e
|
241
|
-
begin
|
242
|
-
# guarantees no throw from explain_sql
|
243
|
-
::NewRelic::Agent.logger.error("Error getting query plan:", e)
|
244
|
-
nil
|
245
|
-
rescue
|
246
|
-
# double exception. throw up your hands
|
247
|
-
nil
|
248
|
-
end
|
99
|
+
# node of a transaction sample. Returns an array of two arrays.
|
100
|
+
# The first array contains the headers, while the second consists of
|
101
|
+
# arrays of strings for each column returned by the explain query.
|
102
|
+
# Note this happens only for statements whose execution time exceeds
|
103
|
+
# a threshold (e.g. 500ms) and only within the slowest transaction
|
104
|
+
# in a report period, selected for shipment to New Relic
|
105
|
+
def explain_sql(statement)
|
106
|
+
return nil unless statement.sql && statement.explainer && statement.config
|
107
|
+
statement.sql = statement.sql.split(";\n")[0] # only explain the first
|
108
|
+
return statement.explain || []
|
249
109
|
end
|
250
110
|
|
251
111
|
KNOWN_OPERATIONS = [
|
@@ -273,14 +133,6 @@ module NewRelic
|
|
273
133
|
end
|
274
134
|
end
|
275
135
|
|
276
|
-
def is_select?(statement)
|
277
|
-
parse_operation_from_query(statement) == 'select'
|
278
|
-
end
|
279
|
-
|
280
|
-
def parameterized?(statement)
|
281
|
-
Obfuscator.instance.obfuscate_single_quote_literals(statement) =~ /\$\d+/
|
282
|
-
end
|
283
|
-
|
284
136
|
class ConnectionManager
|
285
137
|
include Singleton
|
286
138
|
|
@@ -318,33 +170,88 @@ module NewRelic
|
|
318
170
|
end
|
319
171
|
|
320
172
|
class Statement
|
321
|
-
|
173
|
+
include ExplainPlanHelpers
|
174
|
+
|
175
|
+
attr_accessor :sql, :config, :explainer, :binds, :name
|
322
176
|
|
323
|
-
|
177
|
+
DEFAULT_QUERY_NAME = "SQL".freeze
|
178
|
+
|
179
|
+
def initialize(sql, config={}, explainer=nil, binds=[], name=DEFAULT_QUERY_NAME)
|
324
180
|
@sql = Database.capture_query(sql)
|
325
181
|
@config = config
|
326
182
|
@explainer = explainer
|
183
|
+
@binds = binds
|
184
|
+
@name = name
|
327
185
|
end
|
328
186
|
|
187
|
+
# This takes a connection config hash from ActiveRecord or Sequel and
|
188
|
+
# returns a symbol describing the associated database adapter
|
329
189
|
def adapter
|
330
|
-
|
190
|
+
return unless @config
|
191
|
+
|
192
|
+
@adapter ||= if @config[:adapter]
|
193
|
+
symbolized_adapter(@config[:adapter].to_s.downcase)
|
194
|
+
elsif @config[:uri] && @config[:uri].to_s =~ /^jdbc:([^:]+):/
|
195
|
+
# This case is for Sequel with the jdbc-mysql, jdbc-postgres, or jdbc-sqlite3 gems.
|
196
|
+
symbolized_adapter($1)
|
197
|
+
else
|
198
|
+
nil
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def explain
|
203
|
+
return unless explainable?
|
204
|
+
handle_exception_in_explain do
|
205
|
+
start = Time.now
|
206
|
+
plan = @explainer.call(self)
|
207
|
+
::NewRelic::Agent.record_metric("Supportability/Database/execute_explain_plan", Time.now - start)
|
208
|
+
return process_resultset(plan, adapter) if plan
|
209
|
+
end
|
331
210
|
end
|
332
211
|
|
212
|
+
private
|
213
|
+
|
333
214
|
POSTGRES_PREFIX = 'postgres'.freeze
|
334
|
-
MYSQL_PREFIX
|
335
|
-
|
215
|
+
MYSQL_PREFIX = 'mysql'.freeze
|
216
|
+
MYSQL2_PREFIX = 'mysql2'.freeze
|
217
|
+
SQLITE_PREFIX = 'sqlite'.freeze
|
336
218
|
|
337
219
|
def symbolized_adapter(adapter)
|
338
220
|
if adapter.start_with? POSTGRES_PREFIX
|
339
221
|
:postgres
|
340
|
-
elsif adapter
|
222
|
+
elsif adapter == MYSQL_PREFIX
|
341
223
|
:mysql
|
224
|
+
# For the purpose of fetching explain plans, we need to maintain the distinction
|
225
|
+
# between usage of mysql and mysql2. Obfuscation is the same, though.
|
226
|
+
elsif adapter == MYSQL2_PREFIX
|
227
|
+
:mysql2
|
342
228
|
elsif adapter.start_with? SQLITE_PREFIX
|
343
229
|
:sqlite
|
344
230
|
else
|
345
231
|
adapter.to_sym
|
346
232
|
end
|
347
233
|
end
|
234
|
+
|
235
|
+
def explainable?
|
236
|
+
return false unless @explainer && is_select?(@sql)
|
237
|
+
|
238
|
+
if @sql[-3,3] == '...'
|
239
|
+
NewRelic::Agent.logger.debug('Unable to collect explain plan for truncated query.')
|
240
|
+
return false
|
241
|
+
end
|
242
|
+
|
243
|
+
if parameterized?(@sql) && @binds.empty?
|
244
|
+
NewRelic::Agent.logger.debug('Unable to collect explain plan for parameter-less parameterized query.')
|
245
|
+
return false
|
246
|
+
end
|
247
|
+
|
248
|
+
if !SUPPORTED_ADAPTERS_FOR_EXPLAIN.include?(adapter)
|
249
|
+
NewRelic::Agent.logger.debug("Not collecting explain plan because an unknown connection adapter ('#{adapter}') was used.")
|
250
|
+
return false
|
251
|
+
end
|
252
|
+
|
253
|
+
true
|
254
|
+
end
|
348
255
|
end
|
349
256
|
end
|
350
257
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
require 'new_relic/agent/database/obfuscator'
|
6
|
+
require 'new_relic/agent/database/postgres_explain_obfuscator'
|
7
|
+
|
8
|
+
module NewRelic
|
9
|
+
module Agent
|
10
|
+
module Database
|
11
|
+
module ExplainPlanHelpers
|
12
|
+
|
13
|
+
SUPPORTED_ADAPTERS_FOR_EXPLAIN = [:postgres, :mysql2, :mysql, :sqlite]
|
14
|
+
|
15
|
+
def is_select?(sql)
|
16
|
+
NewRelic::Agent::Database.parse_operation_from_query(sql) == 'select'
|
17
|
+
end
|
18
|
+
|
19
|
+
def parameterized?(sql)
|
20
|
+
Obfuscator.instance.obfuscate_single_quote_literals(sql) =~ /\$\d+/
|
21
|
+
end
|
22
|
+
|
23
|
+
def handle_exception_in_explain
|
24
|
+
yield
|
25
|
+
rescue => e
|
26
|
+
begin
|
27
|
+
# guarantees no throw from explain_sql
|
28
|
+
::NewRelic::Agent.logger.error("Error getting query plan:", e)
|
29
|
+
nil
|
30
|
+
rescue
|
31
|
+
# double exception. throw up your hands
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_resultset(results, adapter)
|
37
|
+
if adapter == :postgres
|
38
|
+
return process_explain_results_postgres(results)
|
39
|
+
elsif defined?(::ActiveRecord::Result) && results.is_a?(::ActiveRecord::Result)
|
40
|
+
# Note if adapter is mysql, will only have headers, not values
|
41
|
+
return [results.columns, results.rows]
|
42
|
+
elsif results.is_a?(String)
|
43
|
+
return string_explain_plan_results(results)
|
44
|
+
end
|
45
|
+
|
46
|
+
case adapter
|
47
|
+
when :mysql2
|
48
|
+
process_explain_results_mysql2(results)
|
49
|
+
when :mysql
|
50
|
+
process_explain_results_mysql(results)
|
51
|
+
when :sqlite
|
52
|
+
process_explain_results_sqlite(results)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
QUERY_PLAN = 'QUERY PLAN'.freeze
|
57
|
+
|
58
|
+
def process_explain_results_postgres(results)
|
59
|
+
if defined?(::ActiveRecord::Result) && results.is_a?(::ActiveRecord::Result)
|
60
|
+
query_plan_string = results.rows.join("\n")
|
61
|
+
elsif results.is_a?(String)
|
62
|
+
query_plan_string = results
|
63
|
+
else
|
64
|
+
lines = []
|
65
|
+
results.each { |row| lines << row[QUERY_PLAN] }
|
66
|
+
query_plan_string = lines.join("\n")
|
67
|
+
end
|
68
|
+
|
69
|
+
unless NewRelic::Agent::Database.record_sql_method == :raw
|
70
|
+
query_plan_string = NewRelic::Agent::Database::PostgresExplainObfuscator.obfuscate(query_plan_string)
|
71
|
+
end
|
72
|
+
values = query_plan_string.split("\n").map { |line| [line] }
|
73
|
+
|
74
|
+
[[QUERY_PLAN], values]
|
75
|
+
end
|
76
|
+
|
77
|
+
# Sequel returns explain plans as just one big pre-formatted String
|
78
|
+
# In that case, we send a nil headers array, and the single string
|
79
|
+
# wrapped in an array for the values.
|
80
|
+
# Note that we don't use this method for Postgres explain plans, since
|
81
|
+
# they need to be passed through the explain plan obfuscator first.
|
82
|
+
def string_explain_plan_results(results)
|
83
|
+
[nil, [results]]
|
84
|
+
end
|
85
|
+
|
86
|
+
def process_explain_results_mysql(results)
|
87
|
+
headers = []
|
88
|
+
values = []
|
89
|
+
if results.is_a?(Array)
|
90
|
+
# We're probably using the jdbc-mysql gem for JRuby, which will give
|
91
|
+
# us an array of hashes.
|
92
|
+
headers = results.first.keys
|
93
|
+
results.each do |row|
|
94
|
+
values << headers.map { |h| row[h] }
|
95
|
+
end
|
96
|
+
else
|
97
|
+
# We're probably using the native mysql driver gem, which will give us
|
98
|
+
# a Mysql::Result object that responds to each_hash
|
99
|
+
results.each_hash do |row|
|
100
|
+
headers = row.keys
|
101
|
+
values << headers.map { |h| row[h] }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
[headers, values]
|
105
|
+
end
|
106
|
+
|
107
|
+
def process_explain_results_mysql2(results)
|
108
|
+
headers = results.fields
|
109
|
+
values = []
|
110
|
+
results.each { |row| values << row }
|
111
|
+
[headers, values]
|
112
|
+
end
|
113
|
+
|
114
|
+
SQLITE_EXPLAIN_COLUMNS = %w[addr opcode p1 p2 p3 p4 p5 comment]
|
115
|
+
|
116
|
+
def process_explain_results_sqlite(results)
|
117
|
+
headers = SQLITE_EXPLAIN_COLUMNS
|
118
|
+
values = []
|
119
|
+
results.each do |row|
|
120
|
+
values << headers.map { |h| row[h] }
|
121
|
+
end
|
122
|
+
[headers, values]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|