rsql 0.2.11 → 0.3.1
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.
- data/bin/rsql +22 -12
- data/lib/rsql.rb +19 -1
- data/lib/rsql/commands.rb +10 -0
- data/lib/rsql/eval_context.rb +49 -7
- data/lib/rsql/mysql_results.rb +120 -110
- data/test/test_eval_context.rb +35 -0
- data/test/test_mysql_results.rb +27 -59
- metadata +6 -6
data/bin/rsql
CHANGED
|
@@ -63,11 +63,13 @@ ARGV.map!{|a| a.sub(/^--/,'-')}
|
|
|
63
63
|
if i = ARGV.index('-rc')
|
|
64
64
|
ARGV.delete_at(i)
|
|
65
65
|
rc_fn = ARGV.delete_at(i)
|
|
66
|
-
|
|
66
|
+
elsif ENV['HOME']
|
|
67
67
|
rc_fn = File.join(ENV['HOME'], ".#{bn}rc")
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
if rc_fn && File.exists?(rc_fn)
|
|
71
|
+
eval_context.load(rc_fn, :skip_init_registrations)
|
|
72
|
+
end
|
|
71
73
|
|
|
72
74
|
def get_password(prompt)
|
|
73
75
|
STDOUT.print(prompt)
|
|
@@ -160,10 +162,14 @@ if i = ARGV.index('-ssh')
|
|
|
160
162
|
(opts.ssh_host, opts.ssh_user, opts.ssh_password, opts.ssh_port) =
|
|
161
163
|
split_login(ARGV.delete_at(i))
|
|
162
164
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
opts.ssh_cfghash = {}
|
|
166
|
+
|
|
167
|
+
if ENV['HOME']
|
|
168
|
+
default_config = File.join(ENV['HOME'], '.ssh', 'config')
|
|
169
|
+
if File.exists?(default_config)
|
|
170
|
+
opts.ssh_config = default_config
|
|
171
|
+
opts.ssh_cfghash = Net::SSH::Config.load(default_config, opts.ssh_host)
|
|
172
|
+
end
|
|
167
173
|
end
|
|
168
174
|
|
|
169
175
|
if opts.ssh_user.nil? || opts.ssh_user.empty?
|
|
@@ -237,7 +243,7 @@ MySQLResults.database_name = ARGV.shift
|
|
|
237
243
|
|
|
238
244
|
if !$stdin.tty? && opts.batch_input.nil?
|
|
239
245
|
# accept commands from stdin
|
|
240
|
-
opts.batch_input = $stdin.read
|
|
246
|
+
opts.batch_input = $stdin.read
|
|
241
247
|
end
|
|
242
248
|
|
|
243
249
|
# make sure we remove any duplicates when we add to the history to
|
|
@@ -381,7 +387,8 @@ if opts.ssh_host
|
|
|
381
387
|
ipc_state = ''
|
|
382
388
|
15.times do
|
|
383
389
|
sleep(1)
|
|
384
|
-
|
|
390
|
+
# if the fork fails, it'll call our at_exit which removes the ipc file
|
|
391
|
+
ipc_state = File.exists?(ipc_fn) ? IO.read(ipc_fn).strip : 'fail'
|
|
385
392
|
break if ipc_state == 'ready' || ipc_state == 'fail'
|
|
386
393
|
end
|
|
387
394
|
|
|
@@ -428,9 +435,11 @@ rescue Exception => ex
|
|
|
428
435
|
exit 1
|
|
429
436
|
end
|
|
430
437
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
438
|
+
if ENV['HOME']
|
|
439
|
+
history_fn = File.join(ENV['HOME'], ".#{bn}_history")
|
|
440
|
+
if File.exists?(history_fn) && 0 < File.size(history_fn)
|
|
441
|
+
YAML.load_file(history_fn).each {|i| Readline::HISTORY.push(i)}
|
|
442
|
+
end
|
|
434
443
|
end
|
|
435
444
|
|
|
436
445
|
Readline.completion_proc = eval_context.method(:complete)
|
|
@@ -515,6 +524,7 @@ end
|
|
|
515
524
|
|
|
516
525
|
child_pids << kill_pid
|
|
517
526
|
opts.delete_field(:mysql_password)
|
|
527
|
+
# fixme: consider emulating a shell prompt and have this cancel any current cmd line
|
|
518
528
|
Signal.trap('INT', nil)
|
|
519
529
|
|
|
520
530
|
Signal.trap('CHLD') do
|
|
@@ -544,7 +554,7 @@ end
|
|
|
544
554
|
|
|
545
555
|
sleep(0.3)
|
|
546
556
|
|
|
547
|
-
if Readline::HISTORY.any?
|
|
557
|
+
if Readline::HISTORY.any? && history_fn
|
|
548
558
|
if 100 < Readline::HISTORY.size
|
|
549
559
|
(Readline::HISTORY.size - 100).times do |i|
|
|
550
560
|
Readline::HISTORY.delete_at(i)
|
data/lib/rsql.rb
CHANGED
|
@@ -5,7 +5,25 @@
|
|
|
5
5
|
# information.
|
|
6
6
|
#
|
|
7
7
|
module RSQL
|
|
8
|
-
VERSION =
|
|
8
|
+
VERSION = [0,3,1]
|
|
9
|
+
|
|
10
|
+
def VERSION.to_s
|
|
11
|
+
self.join('.')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Set up our version to be comparable to version strings.
|
|
15
|
+
#
|
|
16
|
+
VERSION.extend(Comparable)
|
|
17
|
+
def VERSION.eql?(version)
|
|
18
|
+
self.<=>(version) == 0
|
|
19
|
+
end
|
|
20
|
+
def VERSION.<=>(version)
|
|
21
|
+
version = version.split('.').map!{|v|v.to_i}
|
|
22
|
+
r = self[0] <=> version[0]
|
|
23
|
+
r = self[1] <=> version[1] if r == 0
|
|
24
|
+
r = self[2] <=> version[2] if r == 0
|
|
25
|
+
r
|
|
26
|
+
end
|
|
9
27
|
|
|
10
28
|
require 'rsql/mysql_results'
|
|
11
29
|
require 'rsql/eval_context'
|
data/lib/rsql/commands.rb
CHANGED
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
|
|
22
22
|
module RSQL
|
|
23
23
|
|
|
24
|
+
# FIXME:
|
|
25
|
+
#
|
|
26
|
+
# select Count(*) from BcheckResults where LastResult != 'Succeeded' and LastResultTime > DATE_SUB(UTC_TIMESTAMP, INTERVAL 1 DAY);
|
|
27
|
+
# NoMethodError: undefined method `+' for nil:NilClass
|
|
28
|
+
# /usr/lib/ruby/gems/1.8/gems/rsql-0.2.11/lib/rsql/commands.rb:82:in `initialize'
|
|
29
|
+
# /usr/lib/ruby/gems/1.8/gems/rsql-0.2.11/bin/rsql:535:in `join'
|
|
30
|
+
# /usr/lib/ruby/gems/1.8/gems/rsql-0.2.11/bin/rsql:535
|
|
31
|
+
# /bin/rsql:19:in `load'
|
|
32
|
+
# /bin/rsql:19
|
|
33
|
+
|
|
24
34
|
require 'stringio'
|
|
25
35
|
|
|
26
36
|
EvalResults = Struct.new(:value, :stdout)
|
data/lib/rsql/eval_context.rb
CHANGED
|
@@ -125,7 +125,7 @@ module RSQL
|
|
|
125
125
|
if Exception === ret
|
|
126
126
|
@loaded_fns_state[fn] = :failed
|
|
127
127
|
if @verbose
|
|
128
|
-
$stderr.puts("#{
|
|
128
|
+
$stderr.puts("#{ret.class}: #{ret.message}", ex.backtrace)
|
|
129
129
|
else
|
|
130
130
|
bt = ret.backtrace.collect{|line| line.start_with?(fn) ? line : nil}.compact
|
|
131
131
|
$stderr.puts("#{ret.class}: #{ret.message}", bt, '')
|
|
@@ -513,15 +513,57 @@ EOF
|
|
|
513
513
|
nil
|
|
514
514
|
end
|
|
515
515
|
|
|
516
|
-
#
|
|
517
|
-
#
|
|
516
|
+
# Remove all rows that do NOT match the expression. Returns true if
|
|
517
|
+
# any matches were found.
|
|
518
518
|
#
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
519
|
+
# Options:
|
|
520
|
+
# :fixed => indicates that the string should be escaped of any
|
|
521
|
+
# special characters
|
|
522
|
+
# :nocolor => will not add color escape codes to indicate the
|
|
523
|
+
# match
|
|
524
|
+
# :inverse => reverses the regular expression match
|
|
525
|
+
#
|
|
526
|
+
def grep(pattern, *gopts) # :doc:
|
|
527
|
+
nocolor = gopts.include?(:nocolor)
|
|
528
|
+
|
|
529
|
+
if inverted = gopts.include?(:inverse)
|
|
530
|
+
# there's no point in coloring matches we are removing
|
|
531
|
+
nocolor = true
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
if gopts.include?(:fixed)
|
|
535
|
+
regexp = Regexp.new(/#{Regexp.escape(pattern.to_str)}/)
|
|
536
|
+
elsif Regexp === pattern
|
|
537
|
+
regexp = pattern
|
|
522
538
|
else
|
|
523
|
-
|
|
539
|
+
regexp = Regexp.new(/#{pattern.to_str}/)
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
rval = inverted
|
|
543
|
+
|
|
544
|
+
@results.delete_if do |row|
|
|
545
|
+
matched = false
|
|
546
|
+
row.each do |val|
|
|
547
|
+
val = val.to_s unless String === val
|
|
548
|
+
if nocolor
|
|
549
|
+
if matched = !val.match(regexp).nil?
|
|
550
|
+
rval = inverted ? false : true
|
|
551
|
+
break
|
|
552
|
+
end
|
|
553
|
+
else
|
|
554
|
+
# in the color case, we want to colorize all hits in
|
|
555
|
+
# all columns, so we can't early terminate our
|
|
556
|
+
# search
|
|
557
|
+
if val.gsub!(regexp){|m| "\e[31;1m#{m}\e[0m"}
|
|
558
|
+
matched = true
|
|
559
|
+
rval = inverted ? false : true
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
inverted ? matched : !matched
|
|
524
564
|
end
|
|
565
|
+
|
|
566
|
+
return rval
|
|
525
567
|
end
|
|
526
568
|
|
|
527
569
|
# Register bangs to evaluate on all displayers as long as a column
|
data/lib/rsql/mysql_results.rb
CHANGED
|
@@ -63,8 +63,8 @@ module RSQL
|
|
|
63
63
|
#
|
|
64
64
|
def conn; @@conn; end
|
|
65
65
|
|
|
66
|
-
# Set the underlying MySQL connection object to use which
|
|
67
|
-
#
|
|
66
|
+
# Set the underlying MySQL connection object to use which implicitly
|
|
67
|
+
# resets the name cache.
|
|
68
68
|
#
|
|
69
69
|
def conn=(conn)
|
|
70
70
|
if @@conn = conn
|
|
@@ -73,23 +73,21 @@ module RSQL
|
|
|
73
73
|
reset_cache
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
# Get the field separator to use when writing rows in
|
|
77
|
-
# columns.
|
|
76
|
+
# Get the field separator to use when writing rows in columns.
|
|
78
77
|
#
|
|
79
78
|
def field_separator; @@field_separator; end
|
|
80
79
|
|
|
81
|
-
# Set the field separator to use when writing rows in
|
|
82
|
-
# columns.
|
|
80
|
+
# Set the field separator to use when writing rows in columns.
|
|
83
81
|
#
|
|
84
82
|
def field_separator=(sep); @@field_separator = sep; end
|
|
85
83
|
|
|
86
|
-
# Get the maximum number of rows to process before
|
|
87
|
-
#
|
|
84
|
+
# Get the maximum number of rows to process before throwing a
|
|
85
|
+
# MaxRowsException.
|
|
88
86
|
#
|
|
89
87
|
def max_rows; @@max_rows; end
|
|
90
88
|
|
|
91
|
-
# Set the maximum number of rows to process before
|
|
92
|
-
#
|
|
89
|
+
# Set the maximum number of rows to process before throwing a
|
|
90
|
+
# MaxRowsException.
|
|
93
91
|
#
|
|
94
92
|
def max_rows=(cnt); @@max_rows = cnt; end
|
|
95
93
|
|
|
@@ -131,8 +129,8 @@ module RSQL
|
|
|
131
129
|
@@name_cache.keys.sort
|
|
132
130
|
end
|
|
133
131
|
|
|
134
|
-
# Get the list of tables available for the current
|
|
135
|
-
#
|
|
132
|
+
# Get the list of tables available for the current database or a
|
|
133
|
+
# specific one.
|
|
136
134
|
#
|
|
137
135
|
def tables(database=@@database_name)
|
|
138
136
|
@@name_cache[database] || []
|
|
@@ -206,7 +204,7 @@ module RSQL
|
|
|
206
204
|
@@conn.select_db(@@database_name) if @@database_name
|
|
207
205
|
end
|
|
208
206
|
|
|
209
|
-
@@history.shift if @@max_history <= @@history.size
|
|
207
|
+
@@history.shift if @@max_history <= @@history.size
|
|
210
208
|
@@history << sql
|
|
211
209
|
|
|
212
210
|
start = Time.now.to_f
|
|
@@ -215,80 +213,115 @@ module RSQL
|
|
|
215
213
|
|
|
216
214
|
affected_rows = @@conn.affected_rows
|
|
217
215
|
unless results && 0 < results.num_rows
|
|
218
|
-
return new(
|
|
216
|
+
return new(nil, nil, affected_rows, sql, elapsed)
|
|
219
217
|
end
|
|
220
218
|
|
|
221
219
|
if max_rows < results.num_rows
|
|
222
220
|
raise MaxRowsException.new(results.num_rows, max_rows)
|
|
223
221
|
end
|
|
224
222
|
|
|
225
|
-
# extract mysql results into our own table so we can
|
|
226
|
-
# lengths of columns and give users a chance to
|
|
227
|
-
# before it's displayed (via the bang maps)
|
|
223
|
+
# extract mysql results into our own table so we can
|
|
224
|
+
# predetermine the lengths of columns and give users a chance to
|
|
225
|
+
# reformat column data before it's displayed (via the bang maps)
|
|
228
226
|
|
|
229
227
|
fields = results.fetch_fields
|
|
230
|
-
fields
|
|
231
|
-
def field.longest_length=(len); @longest_length = len; end
|
|
232
|
-
def field.longest_length; @longest_length; end
|
|
233
|
-
field.longest_length = field.name.length
|
|
234
|
-
field
|
|
235
|
-
end
|
|
228
|
+
extend_fields!(fields)
|
|
236
229
|
|
|
237
230
|
results_table = []
|
|
238
231
|
while vals = results.fetch_row
|
|
239
232
|
row = []
|
|
240
233
|
fields.each_with_index do |field, i|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
val =
|
|
234
|
+
val = vals[i]
|
|
235
|
+
orig_vlen = val.respond_to?(:length) ? val.length : 0
|
|
236
|
+
if val && field.is_num?
|
|
237
|
+
val = field.decimals == 0 ? val.to_i : val.to_f
|
|
238
|
+
end
|
|
239
|
+
unless raw
|
|
240
|
+
val = eval_context.bang_eval(field.name, val)
|
|
245
241
|
if val.nil?
|
|
246
242
|
val = 'NULL'
|
|
247
243
|
elsif HEX_RANGE.include?(field.type) && val =~ /[^[:print:]\s]/
|
|
248
244
|
val = eval_context.to_hexstr(val)
|
|
249
245
|
end
|
|
250
246
|
end
|
|
251
|
-
|
|
252
|
-
if field.longest_length < vlen
|
|
253
|
-
if String === val
|
|
254
|
-
# consider only the longest line length since some
|
|
255
|
-
# output contains multiple lines like "show create table"
|
|
256
|
-
longest_line = val.split(/\r?\n/).collect{|l|l.length}.max
|
|
257
|
-
if field.longest_length < longest_line
|
|
258
|
-
field.longest_length = longest_line
|
|
259
|
-
end
|
|
260
|
-
else
|
|
261
|
-
field.longest_length = val.length
|
|
262
|
-
end
|
|
263
|
-
end
|
|
247
|
+
update_longest!(field, val, orig_vlen)
|
|
264
248
|
row << val
|
|
265
249
|
end
|
|
266
250
|
results_table << row
|
|
267
251
|
end
|
|
268
252
|
|
|
269
|
-
return new(
|
|
253
|
+
return new(fields, results_table, affected_rows, sql, elapsed)
|
|
270
254
|
end
|
|
271
255
|
|
|
256
|
+
########################################
|
|
257
|
+
private
|
|
258
|
+
|
|
259
|
+
def extend_fields!(fields)
|
|
260
|
+
fields.collect! do |field|
|
|
261
|
+
def field.name; to_s; end unless field.respond_to?(:name)
|
|
262
|
+
def field.longest_length=(len); @longest_length = len; end
|
|
263
|
+
def field.longest_length; @longest_length; end
|
|
264
|
+
field.longest_length = field.name.length
|
|
265
|
+
field
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def update_longest!(field, val, default_vlen=nil)
|
|
270
|
+
vlen = if val.respond_to?(:length)
|
|
271
|
+
val.length
|
|
272
|
+
elsif default_vlen
|
|
273
|
+
default_vlen
|
|
274
|
+
else
|
|
275
|
+
val.to_s.length
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
if field.longest_length < vlen
|
|
279
|
+
if String === val
|
|
280
|
+
# consider only the longest line length since some
|
|
281
|
+
# output contains multiple lines like "show create
|
|
282
|
+
# table"
|
|
283
|
+
longest_line = val.split(/\r?\n/).collect{|l|l.length}.max
|
|
284
|
+
if field.longest_length < longest_line
|
|
285
|
+
field.longest_length = longest_line
|
|
286
|
+
end
|
|
287
|
+
else
|
|
288
|
+
field.longest_length = vlen
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
272
293
|
end # class << self
|
|
273
294
|
|
|
274
295
|
########################################
|
|
275
296
|
|
|
276
|
-
def initialize(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
@elapsed = elapsed;
|
|
280
|
-
@affected_rows = affected_rows;
|
|
297
|
+
def initialize(fields, table, affected_rows=nil, sql=nil, elapsed=nil,
|
|
298
|
+
field_separator=@@field_separator)
|
|
299
|
+
|
|
281
300
|
@fields = fields
|
|
282
301
|
@table = table
|
|
302
|
+
@affected_rows = affected_rows || table.size
|
|
303
|
+
@sql = sql
|
|
304
|
+
@elapsed = elapsed
|
|
283
305
|
@field_separator = field_separator
|
|
284
306
|
|
|
285
|
-
# we set this here so that
|
|
286
|
-
#
|
|
287
|
-
|
|
288
|
-
if @sql.match(/use\s+(\S+)/i)
|
|
307
|
+
# we set this here so that it occurs _after_ we are successful and
|
|
308
|
+
# so we can show an appropriate messge in a displayer
|
|
309
|
+
if @sql && @sql.match(/use\s+(\S+)/i)
|
|
289
310
|
@database_changed = true
|
|
290
311
|
@@database_name = $1
|
|
291
312
|
end
|
|
313
|
+
|
|
314
|
+
# if we're not given fields from a query we need to find the column
|
|
315
|
+
# widths
|
|
316
|
+
if @fields && @table && @fields.size > 0 &&
|
|
317
|
+
!@fields.first.respond_to?(:longest_length)
|
|
318
|
+
self.class.send(:extend_fields!, @fields)
|
|
319
|
+
@table.each do |row|
|
|
320
|
+
@fields.each_with_index do |field, i|
|
|
321
|
+
self.class.send(:update_longest!, field, row[i])
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
end
|
|
292
325
|
end
|
|
293
326
|
|
|
294
327
|
# Get the query associated with these results.
|
|
@@ -321,19 +354,33 @@ module RSQL
|
|
|
321
354
|
@table ? @table.size : 0
|
|
322
355
|
end
|
|
323
356
|
|
|
324
|
-
#
|
|
357
|
+
# Convenience helper to grab the value in the very first entry for those
|
|
358
|
+
# times when the query only has one expected value returned.
|
|
325
359
|
#
|
|
326
|
-
def
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
360
|
+
def scalar
|
|
361
|
+
self[0,0]
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Get an entire row as a hash or a single value from the table of
|
|
365
|
+
# results. The value of row_j may be an integer index or a string column
|
|
366
|
+
# name.
|
|
367
|
+
#
|
|
368
|
+
def [](row_i, row_j=nil)
|
|
369
|
+
if @table
|
|
370
|
+
if row = @table[row_i]
|
|
371
|
+
if row_j
|
|
372
|
+
return row[row_j] if Integer === row_j
|
|
373
|
+
if row_j = @fields.index{|f| f.name == row_j}
|
|
374
|
+
return row[row_j]
|
|
375
|
+
end
|
|
376
|
+
else
|
|
377
|
+
hash = {}
|
|
378
|
+
@fields.each_with_index{|f,i| hash[f.name] = row[i]}
|
|
379
|
+
return hash
|
|
380
|
+
end
|
|
381
|
+
end
|
|
336
382
|
end
|
|
383
|
+
return nil
|
|
337
384
|
end
|
|
338
385
|
|
|
339
386
|
# Iterate through each row of the table hashed with the field names.
|
|
@@ -348,59 +395,21 @@ module RSQL
|
|
|
348
395
|
end
|
|
349
396
|
end
|
|
350
397
|
|
|
351
|
-
#
|
|
352
|
-
# matches were found.
|
|
353
|
-
#
|
|
354
|
-
# Options:
|
|
355
|
-
# :fixed => indicates that the string should be escaped of any special characters
|
|
356
|
-
# :nocolor => will not add color escape codes to indicate the match
|
|
357
|
-
# :inverse => reverses the regular expression match
|
|
398
|
+
# Conditionally delete rows from the results.
|
|
358
399
|
#
|
|
359
|
-
def
|
|
400
|
+
def delete_if(opts=nil, &block)
|
|
360
401
|
if @table
|
|
361
|
-
nocolor = gopts.include?(:nocolor)
|
|
362
|
-
|
|
363
|
-
if inverted = gopts.include?(:inverse)
|
|
364
|
-
# there's no point in coloring matches we are removing
|
|
365
|
-
nocolor = true
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
if gopts.include?(:fixed)
|
|
369
|
-
regexp = Regexp.new(/#{Regexp.escape(pattern.to_str)}/)
|
|
370
|
-
elsif Regexp === pattern
|
|
371
|
-
regexp = pattern
|
|
372
|
-
else
|
|
373
|
-
regexp = Regexp.new(/#{pattern.to_str}/)
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
rval = inverted
|
|
377
|
-
|
|
378
402
|
@table.delete_if do |row|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
break
|
|
386
|
-
end
|
|
387
|
-
else
|
|
388
|
-
# in the color case, we want to colorize all hits in
|
|
389
|
-
# all columns, so we can't early terminate our
|
|
390
|
-
# search
|
|
391
|
-
if val.gsub!(regexp){|m| "\e[31;1m#{m}\e[0m"}
|
|
392
|
-
matched = true
|
|
393
|
-
rval = inverted ? false : true
|
|
394
|
-
end
|
|
395
|
-
end
|
|
403
|
+
if opts == :row_hash
|
|
404
|
+
hash = {}
|
|
405
|
+
@fields.each_with_index{|f,i| hash[f.name] = row[i]}
|
|
406
|
+
yield(hash)
|
|
407
|
+
else
|
|
408
|
+
yield(row)
|
|
396
409
|
end
|
|
397
|
-
inverted ? matched : !matched
|
|
398
410
|
end
|
|
399
|
-
|
|
400
|
-
return rval
|
|
401
|
-
else
|
|
402
|
-
return false
|
|
403
411
|
end
|
|
412
|
+
self
|
|
404
413
|
end
|
|
405
414
|
|
|
406
415
|
# Show a set of results in a decent fashion.
|
|
@@ -458,19 +467,20 @@ module RSQL
|
|
|
458
467
|
# Show a summary line of the results.
|
|
459
468
|
#
|
|
460
469
|
def display_stats(io=$stdout, hdr='')
|
|
470
|
+
estr = @elapsed ? " (#{'%0.2f'%@elapsed} sec)" : ''
|
|
461
471
|
if @table
|
|
462
472
|
if @database_changed
|
|
463
473
|
io.puts(hdr, "Database changed");
|
|
464
474
|
hdr = ''
|
|
465
475
|
end
|
|
466
476
|
s = 1 == @table.size ? 'row' : 'rows'
|
|
467
|
-
io.puts(hdr, "#{@table.size} #{s} in set
|
|
477
|
+
io.puts(hdr, "#{@table.size} #{s} in set#{estr}")
|
|
468
478
|
else
|
|
469
479
|
if @database_changed
|
|
470
480
|
io.puts(hdr, "Database changed");
|
|
471
481
|
else
|
|
472
482
|
s = 1 == @affected_rows ? 'row' : 'rows'
|
|
473
|
-
io.puts(hdr, "Query OK, #{@affected_rows} #{s} affected
|
|
483
|
+
io.puts(hdr, "Query OK, #{@affected_rows} #{s} affected#{estr}")
|
|
474
484
|
end
|
|
475
485
|
end
|
|
476
486
|
end
|
data/test/test_eval_context.rb
CHANGED
|
@@ -179,4 +179,39 @@ class TestEvalContext < Test::Unit::TestCase
|
|
|
179
179
|
File.unlink(tf+'~') if File.exists?(tf+'~')
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
+
def test_grep
|
|
183
|
+
out = StringIO.new
|
|
184
|
+
|
|
185
|
+
res = [[11,'one$'],[12,'one and two'],[13,'four and none']]
|
|
186
|
+
@ctx.safe_eval('grep "two"', res, out)
|
|
187
|
+
assert(out.string.empty?)
|
|
188
|
+
assert_equal([[12,"one and \e[31;1mtwo\e[0m"]], res)
|
|
189
|
+
|
|
190
|
+
res = [[11,'one'],[12,'one and two'],[13,'four and none']]
|
|
191
|
+
out = StringIO.new
|
|
192
|
+
@ctx.safe_eval('grep /four/, :nocolor', res, out)
|
|
193
|
+
assert(out.string.empty?)
|
|
194
|
+
assert_equal([[13,'four and none']], res)
|
|
195
|
+
|
|
196
|
+
res = [[11,'one'],[12,'one and two'],[13,'four and none']]
|
|
197
|
+
out = StringIO.new
|
|
198
|
+
@ctx.safe_eval('grep "and", :inverse, :fixed', res, out)
|
|
199
|
+
assert(out.string.empty?)
|
|
200
|
+
assert_equal([[11,'one']], res)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def test_help
|
|
204
|
+
out = StringIO.new
|
|
205
|
+
@ctx.safe_eval('help', nil, out)
|
|
206
|
+
assert_match(/only rows/, out.string)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def test_exception
|
|
210
|
+
orig_err = $stderr
|
|
211
|
+
$stderr = err = StringIO.new
|
|
212
|
+
@ctx.safe_eval('this should fail', nil, nil)
|
|
213
|
+
$stderr = orig_err
|
|
214
|
+
assert_match(/test_eval_context.rb/, err.string)
|
|
215
|
+
end
|
|
216
|
+
|
|
182
217
|
end # class TestEvalContext
|
data/test/test_mysql_results.rb
CHANGED
|
@@ -76,9 +76,14 @@ class TestMySQLResults < Test::Unit::TestCase
|
|
|
76
76
|
f1 = mock('f1')
|
|
77
77
|
f1.expects(:name).returns('c1').times(12)
|
|
78
78
|
f1.expects(:type).returns(1).times(2)
|
|
79
|
+
f1.stubs(:is_num?).returns(false)
|
|
80
|
+
f1.stubs(:longest_length)
|
|
81
|
+
|
|
79
82
|
f2 = mock('f2')
|
|
80
83
|
f2.expects(:name).returns('c2').times(11)
|
|
81
84
|
f2.expects(:type).returns(1).times(2)
|
|
85
|
+
f2.stubs(:is_num?).returns(false)
|
|
86
|
+
f2.stubs(:longest_length)
|
|
82
87
|
|
|
83
88
|
res = mock('results')
|
|
84
89
|
res.expects(:num_rows).returns(2).times(2)
|
|
@@ -132,68 +137,31 @@ class TestMySQLResults < Test::Unit::TestCase
|
|
|
132
137
|
dout.string.gsub(/\s+/,''))
|
|
133
138
|
end
|
|
134
139
|
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
f1.stubs(:type).returns(1)
|
|
139
|
-
f2 = mock('f2')
|
|
140
|
-
f2.stubs(:name).returns('c2')
|
|
141
|
-
f2.stubs(:type).returns(1)
|
|
142
|
-
|
|
143
|
-
res = mock('results')
|
|
144
|
-
res.stubs(:num_rows).returns(2)
|
|
145
|
-
res.stubs(:fetch_fields).returns([f1,f2])
|
|
146
|
-
|
|
147
|
-
rows = sequence(:rows)
|
|
148
|
-
res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
|
|
149
|
-
res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
|
|
150
|
-
res.expects(:fetch_row).in_sequence(rows).returns(nil)
|
|
140
|
+
def test_init
|
|
141
|
+
mres = MySQLResults.new([], [])
|
|
142
|
+
assert_equal(0, mres.num_rows)
|
|
151
143
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
MySQLResults.conn = conn
|
|
160
|
-
|
|
161
|
-
mres = MySQLResults.query('ignored1', eval_context=nil, raw=true)
|
|
162
|
-
assert_equal(false, mres.grep(/val/))
|
|
163
|
-
|
|
164
|
-
rows = sequence(:rows)
|
|
165
|
-
res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
|
|
166
|
-
res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
|
|
167
|
-
res.expects(:fetch_row).in_sequence(rows).returns(nil)
|
|
168
|
-
|
|
169
|
-
mres = MySQLResults.query('ignored2', eval_context=nil, raw=true)
|
|
170
|
-
assert_equal(true, mres.grep('v1', :fixed))
|
|
171
|
-
assert_equal("\e[31;1mv1\e[0m.1", mres[0]['c1'])
|
|
172
|
-
assert_equal("\e[31;1mv1\e[0m.2", mres[0]['c2'])
|
|
173
|
-
|
|
174
|
-
rows = sequence(:rows)
|
|
175
|
-
res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
|
|
176
|
-
res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
|
|
177
|
-
res.expects(:fetch_row).in_sequence(rows).returns(nil)
|
|
178
|
-
|
|
179
|
-
mres = MySQLResults.query('ignored3', eval_context=nil, raw=true)
|
|
180
|
-
assert_equal(false, mres.grep('v', :fixed, :inverse))
|
|
181
|
-
|
|
182
|
-
rows = sequence(:rows)
|
|
183
|
-
res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
|
|
184
|
-
res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
|
|
185
|
-
res.expects(:fetch_row).in_sequence(rows).returns(nil)
|
|
144
|
+
mres = MySQLResults.new(['col1', 'col2'], [[1,2], [3,4]])
|
|
145
|
+
assert_equal(2, mres.num_rows)
|
|
146
|
+
dout = StringIO.new
|
|
147
|
+
mres.display_by_column(dout)
|
|
148
|
+
assert_equal('col1col2----------1234----------2rowsinset',
|
|
149
|
+
dout.string.gsub(/\s+/,''))
|
|
150
|
+
end
|
|
186
151
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
assert_equal(
|
|
152
|
+
def test_row_access
|
|
153
|
+
mres = MySQLResults.new(['col1', 'col2'], [[1,2], [3,4]])
|
|
154
|
+
assert_equal(3, mres[1,0])
|
|
155
|
+
assert_equal(4, mres[1,'col2'])
|
|
156
|
+
assert_equal({'col1'=>3,'col2'=>4}, mres[1])
|
|
157
|
+
end
|
|
190
158
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
assert_equal(
|
|
195
|
-
|
|
196
|
-
assert_equal(
|
|
159
|
+
def test_delete
|
|
160
|
+
mres = MySQLResults.new(['col1', 'col2'], [[1,2], [3,4]])
|
|
161
|
+
mres.delete_if{|r| r[1] == 4}
|
|
162
|
+
assert_equal(nil, mres[1])
|
|
163
|
+
mres.delete_if(:row_hash){|r| r['col1'] == 1}
|
|
164
|
+
assert_equal(nil, mres[0])
|
|
197
165
|
end
|
|
198
166
|
|
|
199
167
|
end # class TestMySQLResults
|
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:
|
|
4
|
+
hash: 17
|
|
5
5
|
prerelease:
|
|
6
6
|
segments:
|
|
7
7
|
- 0
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
version: 0.
|
|
8
|
+
- 3
|
|
9
|
+
- 1
|
|
10
|
+
version: 0.3.1
|
|
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-
|
|
18
|
+
date: 2012-04-12 00:00:00 Z
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
21
21
|
name: net-ssh
|
|
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
165
165
|
requirements: []
|
|
166
166
|
|
|
167
167
|
rubyforge_project:
|
|
168
|
-
rubygems_version: 1.8.
|
|
168
|
+
rubygems_version: 1.8.21
|
|
169
169
|
signing_key:
|
|
170
170
|
specification_version: 3
|
|
171
171
|
summary: Ruby-based MySQL command line with recipes.
|