rsql 0.2.10 → 0.2.11

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.
File without changes
data/bin/rsql CHANGED
@@ -270,6 +270,39 @@ end
270
270
 
271
271
  MySQLResults.max_rows ||= opts.batch_output ? 5000 : 1000
272
272
 
273
+ # all children processes we need to clean up
274
+ child_pids = []
275
+
276
+ # a file that we can leverage as a poor-man's IPC between the ssh process during
277
+ # setup and then with the mysql "kill" process
278
+ ipc_fn = File.join(Dir.tmpdir, "rsql_ipc_#{$$}")
279
+
280
+ at_exit do
281
+ child_pids.each do |pid|
282
+ begin
283
+ Process.kill('TERM', pid)
284
+ killed = false
285
+ 5.times do
286
+ if Process.waitpid(pid, Process::WNOHANG)
287
+ killed = true
288
+ break
289
+ end
290
+ sleep 1
291
+ end
292
+ Process.kill('KILL', pid) unless killed
293
+ rescue Errno::ESRCH
294
+ # do nothing, process is already gone
295
+ rescue Exception => ex
296
+ if opts.verbose
297
+ $stderr.puts("#{ex.class}: #{ex.message}", ex.backtrace)
298
+ else
299
+ $stderr.puts(ex.message)
300
+ end
301
+ end
302
+ end
303
+ File.unlink(ipc_fn) if File.exists?(ipc_fn)
304
+ end
305
+
273
306
  if opts.ssh_host
274
307
 
275
308
  # might need to open an idle channel here so server doesn't close on
@@ -287,10 +320,6 @@ if opts.ssh_host
287
320
  # calls...so we'll run ssh in the background since its only
288
321
  # purpose is to forward us in for accessing the mysql server
289
322
 
290
- # we'll use a poor-man's ipc to determine when the ssh process is
291
- # ready
292
- ipc_fn = File.join(Dir.tmpdir, "rsql_ssh_#{$$}.pid")
293
-
294
323
  ssh = nil
295
324
  ssh_pid = Process.fork do
296
325
  File.open(ipc_fn,'w'){|f| f.puts('start')}
@@ -347,41 +376,15 @@ if opts.ssh_host
347
376
  end
348
377
  end
349
378
 
350
- at_exit do
351
- if ssh_pid && 0 <= ssh_pid
352
- begin
353
- Process.kill('TERM', ssh_pid)
354
- killed = false
355
- 5.times do
356
- if Process.waitpid(ssh_pid, Process::WNOHANG)
357
- killed = true
358
- break
359
- end
360
- sleep 1
361
- end
362
- Process.kill('KILL', ssh_pid) unless killed
363
- rescue Errno::ESRCH
364
- # do nothing, process is already gone
365
- rescue Exception => ex
366
- if opts.verbose
367
- $stderr.puts("#{ex.class}: #{ex.message}", ex.backtrace)
368
- else
369
- $stderr.puts(ex.message)
370
- end
371
- end
372
- end
373
- File.unlink(ipc_fn) if File.exists?(ipc_fn)
374
- end
379
+ child_pids << ssh_pid
375
380
 
376
381
  ipc_state = ''
377
382
  15.times do
378
383
  sleep(1)
379
- File.open(ipc_fn,'r'){|f| ipc_state = f.gets.strip}
384
+ ipc_state = IO.read(ipc_fn).strip
380
385
  break if ipc_state == 'ready' || ipc_state == 'fail'
381
386
  end
382
387
 
383
- File.unlink(ipc_fn)
384
-
385
388
  unless ipc_state == 'ready'
386
389
  # give the child time to exit
387
390
  sleep(0.5)
@@ -402,7 +405,6 @@ mysql_conn = "#{opts.mysql_host}:#{opts.remote_mysql_port || opts.mysql_port}"
402
405
  begin
403
406
  MySQLResults.conn = Mysql.new(opts.mysql_host, opts.mysql_user, opts.mysql_password,
404
407
  MySQLResults.database_name, opts.mysql_port)
405
- MySQLResults.conn.reconnect = true
406
408
  puts 'connected' unless opts.batch_output
407
409
  rescue Mysql::Error => ex
408
410
  if ex.message.include?('Client does not support authentication')
@@ -426,8 +428,6 @@ rescue Exception => ex
426
428
  exit 1
427
429
  end
428
430
 
429
- eval_context.call_init_registrations
430
-
431
431
  history_fn = File.join(ENV['HOME'], ".#{bn}_history")
432
432
  if File.exists?(history_fn) && 0 < File.size(history_fn)
433
433
  YAML.load_file(history_fn).each {|i| Readline::HISTORY.push(i)}
@@ -436,9 +436,17 @@ end
436
436
  Readline.completion_proc = eval_context.method(:complete)
437
437
 
438
438
  cmd_thread = Thread.new do
439
+ mysql_tid = nil
439
440
  me = Thread.current
440
441
  me[:shutdown] = false
441
442
  until me[:shutdown] do
443
+ if mysql_tid != MySQLResults.conn.thread_id
444
+ # update the thread id for the mysql kill process
445
+ mysql_tid = MySQLResults.conn.thread_id
446
+ File.open(ipc_fn,'w'){|f| f.puts(mysql_tid)}
447
+ eval_context.call_init_registrations
448
+ end
449
+
442
450
  default_displayer = :display_by_column
443
451
  if opts.batch_input
444
452
  default_displayer = :display_by_batch if opts.batch_output
@@ -466,6 +474,7 @@ cmd_thread = Thread.new do
466
474
  prompt = ''
467
475
  end
468
476
  if input.nil? || me[:shutdown]
477
+ me[:shutdown] = true
469
478
  puts
470
479
  break
471
480
  end
@@ -484,33 +493,32 @@ cmd_thread = Thread.new do
484
493
  end
485
494
  end
486
495
 
487
- # keep a secondary connection to allow us to kill off a running query
488
- kill_conn = Mysql.new(opts.mysql_host, opts.mysql_user, opts.mysql_password,
489
- MySQLResults.database_name, opts.mysql_port)
490
- opts.delete_field(:mysql_password)
491
-
492
- Signal.trap('INT') do
493
- # emulate MySQL's behavior
494
- if MySQLResults.conn && MySQLResults.conn.busy?
495
- tid = MySQLResults.conn.thread_id
496
- $stderr.puts "Ctrl-C -- sending \"KILL QUERY #{tid}\" to server..."
497
- kill_conn.kill(tid)
498
- 10.times do
499
- break unless MySQLResults.conn.busy?
500
- sleep(0.5)
496
+ kill_pid = Process.fork do
497
+ # keep a secondary connection to allow us to kill off a running query in a
498
+ # subprocess so we don't get bound up by the main process trying to deal
499
+ # with results
500
+ kill_conn = Mysql.new(opts.mysql_host, opts.mysql_user, opts.mysql_password, nil, opts.mysql_port)
501
+ Signal.trap('TERM'){Kernel.exit!}
502
+ Signal.trap('INT') do
503
+ mysql_tid = IO.read(ipc_fn).strip.to_i
504
+ is_busy = false
505
+ kill_conn.list_processes.each do |row|
506
+ if row[0].to_i == mysql_tid && row[4] != 'Sleep'
507
+ $stderr.puts "Ctrl-C -- sending \"KILL QUERY #{mysql_tid}\" to server..."
508
+ kill_conn.kill(mysql_tid)
509
+ break
510
+ end
501
511
  end
502
- MySQLResults.conn.select_db(MySQLResults.database_name)
503
- eval_context.call_init_registrations
504
- else
505
- $stderr.puts 'Ctrl-C -- exit!'
506
- cmd_thread[:shutdown] = true
507
- sleep(0.3)
508
- cmd_thread.kill
509
512
  end
513
+ loop{sleep 0x7fffffff}
510
514
  end
511
515
 
516
+ child_pids << kill_pid
517
+ opts.delete_field(:mysql_password)
518
+ Signal.trap('INT', nil)
519
+
512
520
  Signal.trap('CHLD') do
513
- unless opts.batch_output
521
+ if ssh_pid && !opts.batch_output && !cmd_thread[:shutdown]
514
522
  $stderr.puts "SSH child (#{ssh_pid}) stopped--shutting down..."
515
523
  end
516
524
  if MySQLResults.conn && MySQLResults.conn.busy?
@@ -5,7 +5,7 @@
5
5
  # information.
6
6
  #
7
7
  module RSQL
8
- VERSION = '0.2.10'
8
+ VERSION = '0.2.11'
9
9
 
10
10
  require 'rsql/mysql_results'
11
11
  require 'rsql/eval_context'
@@ -70,6 +70,7 @@ module RSQL
70
70
  esc = ''
71
71
  end
72
72
 
73
+ found_maps = false
73
74
  if match_before_bang
74
75
  new_bangs = {}
75
76
  match.split(/\s*,\s*/).each do |ent|
@@ -78,20 +79,23 @@ module RSQL
78
79
  # they are using a bang but have no maps
79
80
  # so we assume this is a != or something
80
81
  # similar and let it go through unmapped
81
- esc = match_before_bang + '!' + match
82
+ match = match_before_bang + '!' + match
82
83
  match_before_bang = nil
83
- break
84
- end
85
- if val.strip == 'nil'
86
- new_bangs[key.strip] = nil
87
84
  else
88
- new_bangs[key.strip] = val.to_sym
85
+ found_maps = true
86
+ if val.strip == 'nil'
87
+ new_bangs[key.strip] = nil
88
+ else
89
+ new_bangs[key.strip] = val.to_sym
90
+ end
89
91
  end
90
92
  end
91
- next unless match_before_bang
92
- match = match_before_bang
93
- match_before_bang = nil
94
- bangs.merge!(new_bangs)
93
+ if found_maps
94
+ next unless match_before_bang
95
+ match = match_before_bang
96
+ match_before_bang = nil
97
+ bangs.merge!(new_bangs)
98
+ end
95
99
  end
96
100
 
97
101
  if sep == ?!
File without changes
@@ -67,7 +67,9 @@ module RSQL
67
67
  # implicitly resets the name cache.
68
68
  #
69
69
  def conn=(conn)
70
- @@conn = conn
70
+ if @@conn = conn
71
+ @@conn.reconnect = true
72
+ end
71
73
  reset_cache
72
74
  end
73
75
 
@@ -197,6 +199,13 @@ module RSQL
197
199
  # Get results from a query.
198
200
  #
199
201
  def query(sql, eval_context, raw=false, max_rows=@@max_rows)
202
+ if @@conn.reconnected?
203
+ # make sure we stick with the user's last database in case
204
+ # we had to reconnect (probably because the query thread was
205
+ # killed
206
+ @@conn.select_db(@@database_name) if @@database_name
207
+ end
208
+
200
209
  @@history.shift if @@max_history <= @@history.size
201
210
  @@history << sql
202
211
 
@@ -23,6 +23,7 @@ class TestCommands < Test::Unit::TestCase
23
23
  @ctx = EvalContext.new
24
24
  @conn = mock('Mysql')
25
25
  @conn.expects(:list_dbs).returns([])
26
+ @conn.expects(:reconnect=)
26
27
  MySQLResults.conn = @conn
27
28
  end
28
29
 
@@ -42,6 +43,7 @@ class TestCommands < Test::Unit::TestCase
42
43
  cmds = Commands.new('do some silly stuff', :display_by_column)
43
44
  @conn.expects(:query).with(instance_of(String)).returns(nil)
44
45
  @conn.expects(:affected_rows).returns(1)
46
+ @conn.expects(:reconnected?)
45
47
  cmds.run!(@ctx)
46
48
  assert_match(/Query OK, 1 row affected/, @strout.string)
47
49
  end
@@ -61,25 +63,38 @@ class TestCommands < Test::Unit::TestCase
61
63
  def test_multiple
62
64
  @conn.expects(:query).with('one thing').returns(nil)
63
65
  @conn.expects(:affected_rows).returns(1)
66
+ @conn.expects(:reconnected?)
64
67
  cmds = Commands.new('. "one thing" ; . puts :hello.inspect', :display_by_column)
65
68
  cmds.run!(@ctx)
66
69
  assert_match(/^QueryOK,1rowaffected\(\d+.\d+sec\):hello$/,
67
70
  @strout.string.gsub(/\s+/,''))
68
71
  end
69
72
 
70
- def test_bangs
73
+ def test_bang
71
74
  cmds = Commands.new('silly stuff ! this => that', :display_by_column)
72
75
  @conn.expects(:query).with('silly stuff').returns(nil)
73
76
  @conn.expects(:affected_rows).returns(13)
77
+ @conn.expects(:reconnected?)
74
78
  cmds.run!(@ctx)
75
79
  assert_match(/Query OK, 13 rows affected/, @strout.string)
80
+ end
76
81
 
77
- # now test logic to continue if it _doesn't_ look like a bang
82
+ def test_bang_with_no_map
78
83
  cmds = Commands.new('silly stuff ! more things', :display_by_column)
79
84
  @conn.expects(:query).with('silly stuff ! more things').returns(nil)
80
85
  @conn.expects(:affected_rows).returns(4)
86
+ @conn.expects(:reconnected?)
81
87
  cmds.run!(@ctx)
82
88
  assert_match(/Query OK, 4 rows affected/, @strout.string)
83
89
  end
84
90
 
91
+ def test_bang_with_pipe
92
+ cmds = Commands.new('silly stuff != this | puts @results.sql', :display_by_column)
93
+ @conn.expects(:query).with('silly stuff != this').returns(nil)
94
+ @conn.expects(:affected_rows).returns(21)
95
+ @conn.expects(:reconnected?)
96
+ cmds.run!(@ctx)
97
+ assert_match("silly stuff != this\n", @strout.string)
98
+ end
99
+
85
100
  end # class TestCommands
@@ -22,6 +22,8 @@ class TestEvalContext < Test::Unit::TestCase
22
22
  @conn.expects(:list_dbs).returns([])
23
23
  @conn.expects(:query).with(instance_of(String)).returns(nil)
24
24
  @conn.expects(:affected_rows).returns(0)
25
+ @conn.expects(:reconnect=)
26
+ @conn.expects(:reconnected?)
25
27
  MySQLResults.conn = @conn
26
28
  @ctx = EvalContext.new
27
29
  @ctx.load(File.join(File.dirname(__FILE__),'..','example.rsqlrc'))
@@ -32,6 +34,7 @@ class TestEvalContext < Test::Unit::TestCase
32
34
  $stdout = out = StringIO.new
33
35
  @conn.expects(:query).with(instance_of(String)).returns(nil)
34
36
  @conn.expects(:affected_rows).returns(0)
37
+ @conn.expects(:reconnected?)
35
38
  @ctx.safe_eval('reload', nil, out)
36
39
  assert_match(/loaded: .+?example.rsqlrc/, out.string)
37
40
  ensure
@@ -24,6 +24,7 @@ class TestMySQLResults < Test::Unit::TestCase
24
24
  def test_databases
25
25
  assert_equal([], MySQLResults.databases)
26
26
  conn = mock('Mysql')
27
+ conn.expects(:reconnect=)
27
28
  conn.expects(:list_dbs).returns(['accounts'])
28
29
  conn.expects(:select_db)
29
30
  conn.expects(:list_tables).returns([])
@@ -36,6 +37,7 @@ class TestMySQLResults < Test::Unit::TestCase
36
37
  MySQLResults.reset_cache
37
38
 
38
39
  conn = mock('Mysql')
40
+ conn.expects(:reconnect=)
39
41
  conn.expects(:list_dbs).returns(['accounts'])
40
42
  conn.expects(:select_db)
41
43
  conn.expects(:list_tables).returns(['users','groups'])
@@ -51,6 +53,7 @@ class TestMySQLResults < Test::Unit::TestCase
51
53
  assert_equal([], MySQLResults.complete(nil))
52
54
 
53
55
  conn = mock('Mysql')
56
+ conn.expects(:reconnect=)
54
57
  conn.expects(:list_dbs).returns(['Accounts','Devices','Locations'])
55
58
  conn.expects(:select_db).times(3)
56
59
  tbls = sequence(:tbls)
@@ -87,9 +90,11 @@ class TestMySQLResults < Test::Unit::TestCase
87
90
  res.expects(:fetch_row).in_sequence(rows).returns(nil)
88
91
 
89
92
  conn = mock('Mysql')
93
+ conn.expects(:reconnect=)
90
94
  conn.expects(:list_dbs).returns([])
91
95
  conn.expects(:query).with(instance_of(String)).returns(res)
92
96
  conn.expects(:affected_rows).returns(1)
97
+ conn.expects(:reconnected?)
93
98
  MySQLResults.conn = conn
94
99
 
95
100
  bangs = mock('bangs')
@@ -145,9 +150,11 @@ class TestMySQLResults < Test::Unit::TestCase
145
150
  res.expects(:fetch_row).in_sequence(rows).returns(nil)
146
151
 
147
152
  conn = mock('Mysql')
153
+ conn.expects(:reconnect=)
148
154
  conn.stubs(:list_dbs).returns([])
149
155
  conn.stubs(:query).with(instance_of(String)).returns(res)
150
156
  conn.stubs(:affected_rows).returns(1)
157
+ conn.expects(:reconnected?).times(4)
151
158
  MySQLResults.reset_history
152
159
  MySQLResults.conn = conn
153
160
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsql
3
3
  version: !ruby/object:Gem::Version
4
- hash: 3
4
+ hash: 1
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 10
10
- version: 0.2.10
9
+ - 11
10
+ version: 0.2.11
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brad Robel-Forrest
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-02-18 00:00:00 Z
18
+ date: 2012-02-24 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: net-ssh
@@ -124,7 +124,6 @@ files:
124
124
  - bin/rsql
125
125
  - example.rsqlrc
126
126
  - example.rsqlrc.rdoc
127
- - extra/mysql-client-5.1.59-1.tgz
128
127
  - lib/rsql.rb
129
128
  - lib/rsql/commands.rb
130
129
  - lib/rsql/eval_context.rb
@@ -166,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
166
165
  requirements: []
167
166
 
168
167
  rubyforge_project:
169
- rubygems_version: 1.8.16
168
+ rubygems_version: 1.8.17
170
169
  signing_key:
171
170
  specification_version: 3
172
171
  summary: Ruby-based MySQL command line with recipes.