newrelic_rpm 3.1.1 → 3.1.2.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of newrelic_rpm might be problematic. Click here for more details.

Files changed (30) hide show
  1. data/CHANGELOG +6 -0
  2. data/lib/new_relic/agent/agent.rb +3 -11
  3. data/lib/new_relic/agent/instrumentation/active_merchant.rb +1 -1
  4. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +3 -2
  5. data/lib/new_relic/agent/instrumentation/net.rb +1 -1
  6. data/lib/new_relic/agent/instrumentation/rails/active_record_instrumentation.rb +1 -0
  7. data/lib/new_relic/agent/instrumentation/rails3/active_record_instrumentation.rb +1 -0
  8. data/lib/new_relic/agent/samplers/memory_sampler.rb +1 -1
  9. data/lib/new_relic/agent/shim_agent.rb +4 -4
  10. data/lib/new_relic/agent/stats_engine/metric_stats.rb +11 -24
  11. data/lib/new_relic/control/frameworks/rails.rb +7 -2
  12. data/lib/new_relic/control/frameworks/rails3.rb +5 -2
  13. data/lib/new_relic/control/instance_methods.rb +9 -2
  14. data/lib/new_relic/data_serialization.rb +35 -34
  15. data/lib/new_relic/language_support.rb +66 -0
  16. data/lib/new_relic/transaction_sample.rb +9 -7
  17. data/lib/new_relic/transaction_sample/segment.rb +28 -22
  18. data/lib/new_relic/version.rb +2 -2
  19. data/newrelic_rpm.gemspec +4 -3
  20. data/test/new_relic/agent/agent/start_test.rb +7 -7
  21. data/test/new_relic/agent/agent_test.rb +20 -1
  22. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +20 -18
  23. data/test/new_relic/agent/instrumentation/instrumentation_test.rb +1 -1
  24. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +6 -6
  25. data/test/new_relic/agent/transaction_sample_builder_test.rb +1 -1
  26. data/test/new_relic/data_serialization_test.rb +73 -3
  27. data/test/new_relic/transaction_sample/segment_test.rb +26 -16
  28. data/test/new_relic/transaction_sample_test.rb +11 -17
  29. data/test/test_helper.rb +12 -2
  30. metadata +66 -98
@@ -176,7 +176,7 @@ module NewRelic
176
176
  end
177
177
 
178
178
  # Perform this in the runtime environment of a managed
179
- # application, to explain the sql statement(s) executed within a
179
+ # application, to explain the sql statement executed within a
180
180
  # segment of a transaction sample. Returns an array of
181
181
  # explanations (which is an array rows consisting of an array of
182
182
  # strings for each column returned by the the explain query)
@@ -185,24 +185,23 @@ module NewRelic
185
185
  # transaction in a report period, selected for shipment to New
186
186
  # Relic
187
187
  def explain_sql
188
+ return @explain_sql if @explain_sql
188
189
  sql = params[:sql]
189
190
  return nil unless sql && params[:connection_config]
190
- statements = sql.split(";\n")
191
- statements.map! do |statement|
192
- # a small sleep to make sure we yield back to the parent
193
- # thread regularly, if there are many explains
194
- sleep(0.0001)
195
- explain_statement(statement, params[:connection_config])
196
- end
197
- statements.compact!
198
- statements
191
+ statement = sql.split(";\n")[0] # only explain the first
192
+ @explain_sql = explain_statement(statement, params[:connection_config]) || []
193
+ @explain_sql
199
194
  end
200
195
 
201
196
  def explain_statement(statement, config)
202
197
  if is_select?(statement)
203
198
  handle_exception_in_explain do
204
199
  connection = NewRelic::TransactionSample.get_connection(config)
205
- process_resultset(connection.execute("EXPLAIN #{statement}")) if connection
200
+ plan = nil
201
+ if connection
202
+ plan = process_resultset(connection.execute("EXPLAIN #{statement}"))
203
+ end
204
+ return plan
206
205
  end
207
206
  end
208
207
  end
@@ -218,29 +217,36 @@ module NewRelic
218
217
  # The resultset type varies for different drivers. Only thing you can count on is
219
218
  # that it implements each. Also: can't use select_rows because the native postgres
220
219
  # driver doesn't know that method.
221
-
222
- if items.respond_to?(:each)
223
- rows = []
220
+
221
+ headers = values = []
222
+ if items.respond_to?(:each_hash)
223
+ items.each_hash do |row|
224
+ headers = row.keys
225
+ values = headers.map{|h| row[h] }
226
+ end
227
+ elsif items.respond_to?(:each)
224
228
  items.each do |row|
225
- columns = []
226
- row.each do |column|
227
- columns << column.to_s
229
+ if row.kind_of?(Hash)
230
+ headers = row.keys
231
+ values = headers.map{|h| row[h] }
232
+ else
233
+ values = row
228
234
  end
229
- rows << columns
230
235
  end
231
- rows
232
236
  else
233
- [items]
237
+ values = [items]
234
238
  end
239
+
240
+ headers = nil if headers.empty?
241
+ [headers, values]
235
242
  end
236
243
 
237
-
238
244
  def handle_exception_in_explain
239
245
  yield
240
246
  rescue Exception => e
241
247
  begin
242
248
  # guarantees no throw from explain_sql
243
- NewRelic::Control.instance.log.error("Error getting explain plan: #{e.message}")
249
+ NewRelic::Control.instance.log.error("Error getting query plan: #{e.message}")
244
250
  NewRelic::Control.instance.log.debug(e.backtrace.join("\n"))
245
251
  rescue Exception
246
252
  # double exception. throw up your hands
@@ -3,8 +3,8 @@ module NewRelic
3
3
  module VERSION #:nodoc:
4
4
  MAJOR = 3
5
5
  MINOR = 1
6
- TINY = 1
7
- BUILD = nil # Set to nil for a release, 'beta1', 'alpha', etc for prerelease builds
6
+ TINY = 2
7
+ BUILD = 'beta1' # Set to nil for a release, 'beta1', 'alpha', etc for prerelease builds
8
8
  STRING = [MAJOR, MINOR, TINY, BUILD].compact.join('.')
9
9
  end
10
10
 
data/newrelic_rpm.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{newrelic_rpm}
8
- s.version = "3.1.1"
8
+ s.version = "3.1.2.beta1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Bill Kayser", "Jon Guymon", "Justin George", "Darin Swanson"]
12
- s.date = %q{2011-07-28}
12
+ s.date = %q{2011-08-18}
13
13
  s.description = %q{New Relic is a performance management system, developed by New Relic,
14
14
  Inc (http://www.newrelic.com). New Relic provides you with deep
15
15
  information about the performance of your web application as it runs
@@ -18,7 +18,7 @@ Gem or plugin, hosted on
18
18
  http://github.com/newrelic/rpm/
19
19
  }
20
20
  s.email = %q{support@newrelic.com}
21
- s.executables = ["newrelic_cmd", "newrelic", "mongrel_rpm"]
21
+ s.executables = ["mongrel_rpm", "newrelic", "newrelic_cmd"]
22
22
  s.extra_rdoc_files = [
23
23
  "CHANGELOG",
24
24
  "LICENSE",
@@ -106,6 +106,7 @@ http://github.com/newrelic/rpm/
106
106
  "lib/new_relic/control/server_methods.rb",
107
107
  "lib/new_relic/data_serialization.rb",
108
108
  "lib/new_relic/delayed_job_injection.rb",
109
+ "lib/new_relic/language_support.rb",
109
110
  "lib/new_relic/local_environment.rb",
110
111
  "lib/new_relic/merbtasks.rb",
111
112
  "lib/new_relic/metric_data.rb",
@@ -190,8 +190,8 @@ class NewRelic::Agent::Agent::StartTest < Test::Unit::TestCase
190
190
  def test_install_exit_handler_positive
191
191
  control = mocked_control
192
192
  control.expects(:send_data_on_exit).returns(true)
193
- self.expects(:using_rubinius?).returns(false)
194
- self.expects(:using_jruby?).returns(false)
193
+ NewRelic::LanguageSupport.expects(:using_rubinius?).returns(false)
194
+ NewRelic::LanguageSupport.expects(:using_jruby?).returns(false)
195
195
  self.expects(:using_sinatra?).returns(false)
196
196
  # we are overriding at_exit above, to immediately return, so we can
197
197
  # test the shutdown logic. It's somewhat unfortunate, but we can't
@@ -209,14 +209,14 @@ class NewRelic::Agent::Agent::StartTest < Test::Unit::TestCase
209
209
  def test_install_exit_handler_weird_ruby
210
210
  control = mocked_control
211
211
  control.expects(:send_data_on_exit).times(3).returns(true)
212
- self.expects(:using_rubinius?).returns(false)
213
- self.expects(:using_jruby?).returns(false)
212
+ NewRelic::LanguageSupport.expects(:using_rubinius?).returns(false)
213
+ NewRelic::LanguageSupport.expects(:using_jruby?).returns(false)
214
214
  self.expects(:using_sinatra?).returns(true)
215
215
  install_exit_handler
216
- self.expects(:using_rubinius?).returns(false)
217
- self.expects(:using_jruby?).returns(true)
216
+ NewRelic::LanguageSupport.expects(:using_rubinius?).returns(false)
217
+ NewRelic::LanguageSupport.expects(:using_jruby?).returns(true)
218
218
  install_exit_handler
219
- self.expects(:using_rubinius?).returns(true)
219
+ NewRelic::LanguageSupport.expects(:using_rubinius?).returns(true)
220
220
  install_exit_handler
221
221
  end
222
222
 
@@ -33,7 +33,26 @@ module NewRelic
33
33
  end
34
34
 
35
35
  def test_harvest_timeslice_data
36
- assert_equal({}, @agent.send(:harvest_timeslice_data), 'should return timeslice data')
36
+ assert_equal({}, @agent.send(:harvest_timeslice_data),
37
+ 'should return timeslice data')
38
+ end
39
+
40
+ def test_harvest_timelice_data_should_be_thread_safe
41
+ 2000.times do |i|
42
+ @agent.stats_engine.stats_hash[i.to_s] = NewRelic::StatsBase.new
43
+ end
44
+
45
+ harvest = Thread.new do
46
+ @agent.send(:harvest_timeslice_data)
47
+ end
48
+
49
+ app = Thread.new do
50
+ @agent.stats_engine.stats_hash["a"] = NewRelic::StatsBase.new
51
+ end
52
+
53
+ assert_nothing_raised do
54
+ [app, harvest].each{|t| t.join}
55
+ end
37
56
  end
38
57
 
39
58
  def test_harvest_errors
@@ -133,7 +133,6 @@ class NewRelic::Agent::Instrumentation::ActiveRecordInstrumentationTest < Test::
133
133
  check_metric_count("ActiveRecord/ActiveRecordFixtures::Order/create", 1)
134
134
  end
135
135
 
136
-
137
136
  def test_metric_names_standard
138
137
  # fails due to a bug in rails 3 - log does not provide the correct
139
138
  # transaction type - it returns 'SQL' instead of 'Foo Create', for example.
@@ -148,11 +147,12 @@ class NewRelic::Agent::Instrumentation::ActiveRecordInstrumentationTest < Test::
148
147
  ActiveRecord/ActiveRecordFixtures::Order/create]
149
148
 
150
149
  if NewRelic::Control.instance.rails_version < '2.1.0'
151
- expected += %W[ActiveRecord/save ActiveRecord/ActiveRecordFixtures::Order/save]
150
+ expected += ['ActiveRecord/save',
151
+ 'ActiveRecord/ActiveRecordFixtures::Order/save']
152
152
  end
153
153
 
154
154
  assert_calls_metrics(*expected) do
155
- m = ActiveRecordFixtures::Order.create :id => 0, :name => 'jeff'
155
+ m = ActiveRecordFixtures::Order.create :id => 0, :name => 'donkey'
156
156
  m = ActiveRecordFixtures::Order.find(m.id)
157
157
  m.id = 999
158
158
  m.save!
@@ -315,6 +315,7 @@ class NewRelic::Agent::Instrumentation::ActiveRecordInstrumentationTest < Test::
315
315
 
316
316
  def test_show_sql
317
317
  return if isSqlite?
318
+ return if isPostgres?
318
319
 
319
320
  expected_metrics = %W[ActiveRecord/all Database/SQL/show]
320
321
 
@@ -368,11 +369,11 @@ class NewRelic::Agent::Instrumentation::ActiveRecordInstrumentationTest < Test::
368
369
  sample = sample.prepare_to_send(:record_sql => :raw, :explain_sql => 0.0)
369
370
  sql_segment = sample.root_segment.called_segments.first.called_segments.first.called_segments.first
370
371
  assert_match /^SELECT /, sql_segment.params[:sql]
371
- explanations = sql_segment.params[:explanation]
372
+ explanations = sql_segment.params[:explain_plan]
372
373
  if isMysql? || isPostgres?
373
374
  assert_not_nil explanations, "No explains in segment: #{sql_segment}"
374
- assert_equal 1, explanations.size,"No explains in segment: #{sql_segment}"
375
- assert_equal 1, explanations.first.size
375
+ assert_equal(2, explanations.size,
376
+ "No explains in segment: #{sql_segment}")
376
377
  end
377
378
  end
378
379
 
@@ -389,14 +390,17 @@ class NewRelic::Agent::Instrumentation::ActiveRecordInstrumentationTest < Test::
389
390
 
390
391
  sample = sample.prepare_to_send(:record_sql => :obfuscated, :explain_sql => 0.0)
391
392
  segment = sample.root_segment.called_segments.first.called_segments.first.called_segments.first
392
- explanations = segment.params[:explanation]
393
- assert_not_nil explanations, "No explains in segment: #{segment}"
394
- assert_equal 1, explanations.size,"No explains in segment: #{segment}"
395
- assert_equal 1, explanations.first.size, "should be one row of explanation"
393
+ explanation = segment.params[:explain_plan]
394
+ assert_not_nil explanation, "No explains in segment: #{segment}"
395
+ assert_equal 2, explanation.size,"No explains in segment: #{segment}"
396
396
 
397
- row = explanations.first.first
398
- assert_equal 10, row.size
399
- assert_equal ['1', 'SIMPLE', ActiveRecordFixtures::Order.table_name], row[0..2]
397
+ assert_equal 10, explanation[0].size
398
+ ['id', 'select_type', 'table'].each do |c|
399
+ assert explanation[0].include?(c)
400
+ end
401
+ ['1', 'SIMPLE', ActiveRecordFixtures::Order.table_name].each do |c|
402
+ assert explanation[1].include?(c)
403
+ end
400
404
 
401
405
  s = NewRelic::Agent.get_stats("ActiveRecord/ActiveRecordFixtures::Order/find")
402
406
  assert_equal 1, s.call_count
@@ -416,16 +420,14 @@ class NewRelic::Agent::Instrumentation::ActiveRecordInstrumentationTest < Test::
416
420
 
417
421
  sample = sample.prepare_to_send(:record_sql => :obfuscated, :explain_sql => 0.0)
418
422
  segment = sample.root_segment.called_segments.first.called_segments.first.called_segments.first
419
- explanations = segment.params[:explanation]
423
+ explanations = segment.params[:explain_plan]
420
424
 
421
425
  assert_not_nil explanations, "No explains in segment: #{segment}"
422
426
  assert_equal 1, explanations.size,"No explains in segment: #{segment}"
423
427
  assert_equal 1, explanations.first.size
424
428
 
425
- assert_equal Array, explanations.class
426
- assert_equal Array, explanations[0].class
427
- assert_equal Array, explanations[0][0].class
428
- assert_match /Seq Scan on test_data/, explanations[0][0].join(";")
429
+ assert_equal("Explain Plan", explanations[0][0])
430
+ assert_match /Seq Scan on test_data/, explanations[0][1].join(";")
429
431
 
430
432
  s = NewRelic::Agent.get_stats("ActiveRecord/ActiveRecordFixtures::Order/find")
431
433
  assert_equal 1, s.call_count
@@ -2,7 +2,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'te
2
2
  class NewRelic::Agent::Instrumentation::InstrumentationTest < Test::Unit::TestCase
3
3
  def test_load_all_instrumentation_files
4
4
  # just checking for syntax errors and unguarded code
5
- Dir.glob(NEWRELIC_PLUGIN_DIR + '/lib/new_relic/agent/instrumentation/**/*.rb') do |f|
5
+ Dir.glob('new_relic/agent/instrumentation/**/*.rb') do |f|
6
6
  require f
7
7
  end
8
8
  require 'new_relic/delayed_job_injection'
@@ -21,7 +21,7 @@ unless ENV['FAST_TESTS']
21
21
  http.get('/index.html')
22
22
  }
23
23
  assert_match /<head>/, res.body
24
- assert_equal %w[External/www.google.com/Net::HTTP/GET External/allOther External/www.google.com/all].sort,
24
+ assert_equal %w[External/all External/www.google.com/Net::HTTP/GET External/allOther External/www.google.com/all].sort,
25
25
  metrics_without_gc.sort
26
26
  end
27
27
 
@@ -33,7 +33,7 @@ unless ENV['FAST_TESTS']
33
33
  }
34
34
  assert_match /<head>/, res.body
35
35
  end
36
- assert_equal %w[External/www.google.com/Net::HTTP/GET External/allOther External/www.google.com/all
36
+ assert_equal %w[External/all External/www.google.com/Net::HTTP/GET External/allOther External/www.google.com/all
37
37
  External/www.google.com/Net::HTTP/GET:OtherTransaction/Background/NewRelic::Agent::Instrumentation::NetInstrumentationTest/task].sort, metrics_without_gc.select{|m| m =~ /^External/}.sort
38
38
  end
39
39
 
@@ -45,13 +45,13 @@ unless ENV['FAST_TESTS']
45
45
  }
46
46
  assert_match /<head>/, res.body
47
47
  end
48
- assert_equal %w[External/www.google.com/Net::HTTP/GET External/allWeb External/www.google.com/all
48
+ assert_equal %w[External/all External/www.google.com/Net::HTTP/GET External/allWeb External/www.google.com/all
49
49
  External/www.google.com/Net::HTTP/GET:Controller/NewRelic::Agent::Instrumentation::NetInstrumentationTest/task].sort, metrics_without_gc.select{|m| m =~ /^External/}.sort
50
50
  end
51
51
  def test_get__simple
52
52
  Net::HTTP.get URI.parse('http://www.google.com/index.html')
53
53
  assert_equal metrics_without_gc.sort,
54
- %w[External/www.google.com/Net::HTTP/GET External/allOther External/www.google.com/all].sort
54
+ %w[External/all External/www.google.com/Net::HTTP/GET External/allOther External/www.google.com/all].sort
55
55
  end
56
56
  def test_ignore
57
57
  NewRelic::Agent.disable_all_tracing do
@@ -67,7 +67,7 @@ unless ENV['FAST_TESTS']
67
67
  res = Net::HTTP.start(url.host, url.port) {|http|
68
68
  http.head('/index.html')
69
69
  }
70
- assert_equal %w[External/www.google.com/Net::HTTP/HEAD External/allOther External/www.google.com/all].sort,
70
+ assert_equal %w[External/all External/www.google.com/Net::HTTP/HEAD External/allOther External/www.google.com/all].sort,
71
71
  metrics_without_gc.sort
72
72
  end
73
73
 
@@ -76,7 +76,7 @@ unless ENV['FAST_TESTS']
76
76
  res = Net::HTTP.start(url.host, url.port) {|http|
77
77
  http.post('/index.html','data')
78
78
  }
79
- assert_equal %w[External/www.google.com/Net::HTTP/POST External/allOther External/www.google.com/all].sort,
79
+ assert_equal %w[External/all External/www.google.com/Net::HTTP/POST External/allOther External/www.google.com/all].sort,
80
80
  metrics_without_gc.sort
81
81
  end
82
82
 
@@ -52,7 +52,7 @@ class NewRelic::Agent::TransationSampleBuilderTest < Test::Unit::TestCase
52
52
 
53
53
  begin
54
54
  build_segment "b"
55
- assert_false
55
+ assert false
56
56
  rescue TypeError => e
57
57
  # expected
58
58
  end
@@ -10,11 +10,12 @@ class NewRelic::DataSerializationTest < Test::Unit::TestCase
10
10
  @file = "#{path}/newrelic_agent_store.db"
11
11
  Dir.mkdir(path) if !File.directory?(path)
12
12
  FileUtils.rm_rf(@file)
13
- FileUtils.rm_rf("#{@path}/newrelic_agent_store.age")
13
+ FileUtils.rm_rf("#{@path}/newrelic_agent_store.pid")
14
14
  end
15
15
 
16
16
  def teardown
17
- NewRelic::Control.instance['disable_serialization'] = false # this gets set to true in some tests
17
+ # this gets set to true in some tests
18
+ NewRelic::Control.instance['disable_serialization'] = false
18
19
  end
19
20
 
20
21
  def test_read_and_write_from_file_read_only
@@ -135,7 +136,76 @@ class NewRelic::DataSerializationTest < Test::Unit::TestCase
135
136
  NewRelic::Control.instance.expects(:log_path).returns('./tmp')
136
137
  Dir.mkdir('./tmp') if !File.directory?('./tmp')
137
138
  NewRelic::DataSerialization.update_last_sent!
138
- assert(File.exists?('./tmp/newrelic_agent_store.age'),
139
+ assert(File.exists?('./tmp/newrelic_agent_store.pid'),
139
140
  "Age file not created at user specified location")
140
141
  end
142
+
143
+ def test_pid_age_creates_pid_file_if_none_exists
144
+ assert(!File.exists?("#{@path}/newrelic_agent_store.pid"),
145
+ 'pid file found, should not be there')
146
+ assert(!NewRelic::DataSerialization.pid_too_old?,
147
+ "new pid should not be too old")
148
+ assert(File.exists?("#{@path}/newrelic_agent_store.pid"),
149
+ 'pid file not found, should be there')
150
+ end
151
+
152
+ def test_loading_does_not_seg_fault_if_gc_triggers
153
+ require 'timeout'
154
+
155
+ Thread.abort_on_exception = true
156
+ rcv,snd = IO.pipe
157
+
158
+ write = Thread.new do
159
+ obj = ('a'..'z').inject({}){|h,s|h[s.intern]=s*1024;h}
160
+ data = Marshal.dump(obj)
161
+ snd.write(data[0,data.size/2])
162
+ sleep(0.1)
163
+ snd.write(data[(data.size/2)..-1])
164
+ end
165
+
166
+ read = Thread.new do
167
+ lock = Mutex.new
168
+ lock.synchronize do
169
+ NewRelic::DataSerialization.class_eval { load(rcv) }
170
+ end
171
+ end
172
+
173
+ gc = Thread.new do
174
+ 10.times do
175
+ GC.start
176
+ end
177
+ end
178
+
179
+ begin
180
+ Timeout::timeout(1) do
181
+ write.join
182
+ read.join
183
+ gc.join
184
+ end
185
+ rescue Timeout::Error
186
+ # rubinius deadlocks which seems to be a rubinius bug
187
+ raise unless NewRelic::LanguageSupport.using_rubinius?
188
+ end
189
+ # should not seg fault
190
+ end
191
+
192
+ def test_dump_should_be_thread_safe
193
+ stats_hash = {}
194
+
195
+ 2000.times do |i|
196
+ stats_hash[i.to_s] = NewRelic::StatsBase.new
197
+ end
198
+
199
+ harvest = Thread.new do
200
+ NewRelic::DataSerialization.class_eval { dump(stats_hash) }
201
+ end
202
+
203
+ app = Thread.new do
204
+ stats_hash["a"] = NewRelic::StatsBase.new
205
+ end
206
+
207
+ assert_nothing_raised do
208
+ [app, harvest].each{|t| t.join}
209
+ end
210
+ end
141
211
  end
@@ -373,37 +373,47 @@ class NewRelic::TransactionSample::SegmentTest < Test::Unit::TestCase
373
373
  assert_equal([], s.explain_sql)
374
374
  end
375
375
 
376
- def test_explain_sql_one_select_with_connection
376
+ def test_explain_sql_one_select_with_mysql_connection
377
377
  s = NewRelic::TransactionSample::Segment.new(Time.now, 'Custom/test/metric', nil)
378
- config = mock('config')
378
+ config = {:adapter => 'mysql'}
379
+ config.default('val')
379
380
  s.params = {:sql => 'SELECT', :connection_config => config}
380
381
  connection = mock('connection')
382
+ plan = {
383
+ "select_type"=>"SIMPLE", "key_len"=>nil, "table"=>"blogs", "id"=>"1",
384
+ "possible_keys"=>nil, "type"=>"ALL", "Extra"=>"", "rows"=>"2",
385
+ "ref"=>nil, "key"=>nil
386
+ }
387
+ result = mock('explain plan')
388
+ result.expects(:each_hash).yields(plan)
381
389
  # two rows, two columns
382
- connection.expects(:execute).with('EXPLAIN SELECT').returns([["string", "string"], ["string", "string"]])
390
+ connection.expects(:execute).with('EXPLAIN SELECT').returns(result)
383
391
  NewRelic::TransactionSample.expects(:get_connection).with(config).returns(connection)
384
- assert_equal([[['string', 'string'], ['string', 'string']]], s.explain_sql)
392
+
393
+ assert_equal(plan.keys.sort, s.explain_sql[0].sort)
394
+ assert_equal(plan.values.compact.sort, s.explain_sql[1].compact.sort)
385
395
  end
386
396
 
387
397
  # this basically casts the resultset to an array of rows, which are
388
398
  # arrays of columns
389
399
  def test_process_resultset
390
400
  s = NewRelic::TransactionSample::Segment.new(Time.now, 'Custom/test/metric', nil)
391
- items = mock('bar')
392
- row = ["column"]
393
- items.expects(:respond_to?).with(:each).returns(true)
394
- items.expects(:each).yields(row)
395
- assert_equal([["column"]], s.process_resultset(items))
401
+ items = [["column"]]
402
+ assert_equal([nil, ["column"]], s.process_resultset(items))
396
403
  end
397
404
 
398
- def test_explain_sql_two_selects_with_connection
405
+ def test_explain_sql_one_select_with_pg_connection
399
406
  s = NewRelic::TransactionSample::Segment.new(Time.now, 'Custom/test/metric', nil)
400
- config = mock('config')
401
- s.params = {:sql => "SELECT true();\nSELECT false()", :connection_config => config}
407
+ config = {:adapter => 'postgresql'}
408
+ config.default('val')
409
+ s.params = {:sql => "SELECT true()", :connection_config => config}
402
410
  connection = mock('connection')
403
411
  # two rows, two columns
404
- connection.expects(:execute).returns([["string", "string"], ["string", "string"]]).twice
405
- NewRelic::TransactionSample.expects(:get_connection).with(config).returns(connection).twice
406
- assert_equal([[['string', 'string'], ['string', 'string']], [['string', 'string'], ['string', 'string']]], s.explain_sql)
412
+ connection.expects(:execute).returns([{"QUERY PLAN"=>"Seq Scan on foo (cost=0.00..11.40 rows=140 width=540)"}])
413
+ NewRelic::TransactionSample.expects(:get_connection).with(config).returns(connection)
414
+ assert_equal([['QUERY PLAN'],
415
+ ['Seq Scan on foo (cost=0.00..11.40 rows=140 width=540)']],
416
+ s.explain_sql)
407
417
  end
408
418
 
409
419
  def test_explain_sql_raising_an_error
@@ -431,7 +441,7 @@ class NewRelic::TransactionSample::SegmentTest < Test::Unit::TestCase
431
441
  s = NewRelic::TransactionSample::Segment.new(Time.now, 'Custom/test/metric', nil)
432
442
  fake_error = Exception.new
433
443
  fake_error.expects(:message).returns('a message')
434
- NewRelic::Control.instance.log.expects(:error).with('Error getting explain plan: a message')
444
+ NewRelic::Control.instance.log.expects(:error).with('Error getting query plan: a message')
435
445
  # backtrace can be basically any string, just should get logged
436
446
  NewRelic::Control.instance.log.expects(:debug).with(instance_of(String))
437
447
  s.handle_exception_in_explain do