td 0.10.4 → 0.10.5
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +11 -0
- data/lib/td/command/aggr.rb +202 -0
- data/lib/td/command/common.rb +32 -6
- data/lib/td/command/help.rb +11 -3
- data/lib/td/command/import.rb +8 -0
- data/lib/td/command/job.rb +22 -34
- data/lib/td/command/list.rb +163 -106
- data/lib/td/command/query.rb +5 -1
- data/lib/td/command/result.rb +111 -0
- data/lib/td/command/runner.rb +20 -4
- data/lib/td/command/sched.rb +9 -5
- data/lib/td/command/status.rb +89 -0
- data/lib/td/command/table.rb +21 -2
- data/lib/td/version.rb +1 -1
- metadata +16 -13
data/ChangeLog
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
|
2
|
+
== 2011-12-04 version 0.10.5
|
3
|
+
|
4
|
+
* Added new feature: result
|
5
|
+
* Added new feature: status
|
6
|
+
* Refined usage message
|
7
|
+
* Fixed argument length check
|
8
|
+
* help subcommand shows commandline examples
|
9
|
+
* table:tail subcommand reduced default max row number
|
10
|
+
* job:list subcommand supports --running and --error options
|
11
|
+
|
12
|
+
|
2
13
|
== 2011-11-11 version 0.10.4
|
3
14
|
|
4
15
|
* Updated dependency: td-logger-0.3.7
|
@@ -0,0 +1,202 @@
|
|
1
|
+
|
2
|
+
module TreasureData
|
3
|
+
module Command
|
4
|
+
|
5
|
+
def aggr_list(op)
|
6
|
+
op.cmd_parse
|
7
|
+
|
8
|
+
client = get_client
|
9
|
+
|
10
|
+
ass = client.aggregation_schemas
|
11
|
+
|
12
|
+
rows = []
|
13
|
+
ass.each {|as|
|
14
|
+
rows << {:Name=>as.name, :Relation=>as.relation_key}
|
15
|
+
}
|
16
|
+
|
17
|
+
puts cmd_render_table(rows, :fields => [:Name, :Relation])
|
18
|
+
|
19
|
+
if rows.empty?
|
20
|
+
$stderr.puts "There are no aggregation schemas."
|
21
|
+
$stderr.puts "Use '#{$prog} aggr:create <name>' to create a aggregation schema."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def aggr_show(op)
|
26
|
+
name = op.cmd_parse
|
27
|
+
|
28
|
+
client = get_client
|
29
|
+
|
30
|
+
begin
|
31
|
+
as = client.aggregation_schema(name)
|
32
|
+
rescue
|
33
|
+
cmd_debug_error $!
|
34
|
+
$stderr.puts "Aggregation '#{name}' does not exist."
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
|
38
|
+
log_rows = []
|
39
|
+
as.logs.each {|las|
|
40
|
+
log_rows << {
|
41
|
+
:Table=>las.table.identifier,
|
42
|
+
:Name=>las.name,
|
43
|
+
:o1_key=>las.okeys[0].to_s,
|
44
|
+
:o2_key=>las.okeys[1].to_s,
|
45
|
+
:o3_key=>las.okeys[2].to_s,
|
46
|
+
:value_key=>las.value_key.to_s,
|
47
|
+
:count_key=>las.count_key.to_s,
|
48
|
+
:Comment=>las.comment.to_s
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
attr_rows = []
|
53
|
+
as.attributes.each {|aas|
|
54
|
+
params = aas.parameters.to_a.map {|k,v| "#{k}=#{v}" }.join(' ')
|
55
|
+
attr_rows << {
|
56
|
+
:Table=>aas.table.identifier,
|
57
|
+
:Name=>aas.name,
|
58
|
+
:Method=>aas.method_name,
|
59
|
+
:Parameters=>params,
|
60
|
+
:Comment=>aas.comment.to_s,
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
puts "Log entries:"
|
65
|
+
puts cmd_render_table(log_rows, :fields => [:Table, :Name, :o1_key, :o2_key, :o3_key, :value_key, :count_key, :Comment], :max_width=>400)
|
66
|
+
|
67
|
+
puts ''
|
68
|
+
|
69
|
+
puts "Attribute entries:"
|
70
|
+
puts cmd_render_table(attr_rows, :fields => [:Table, :Name, :Method, :Parameters, :Comment], :max_width=>400)
|
71
|
+
end
|
72
|
+
|
73
|
+
def aggr_create(op)
|
74
|
+
name, relation_key = op.cmd_parse
|
75
|
+
|
76
|
+
client = get_client
|
77
|
+
|
78
|
+
begin
|
79
|
+
client.create_aggregation_schema(name, relation_key)
|
80
|
+
rescue AlreadyExistsError
|
81
|
+
cmd_debug_error $!
|
82
|
+
$stderr.puts "Aggregation '#{name}' already exists."
|
83
|
+
exit 1
|
84
|
+
end
|
85
|
+
|
86
|
+
$stderr.puts "Aggregation schema '#{name}' is created."
|
87
|
+
end
|
88
|
+
|
89
|
+
def aggr_delete(op)
|
90
|
+
name = op.cmd_parse
|
91
|
+
|
92
|
+
client = get_client
|
93
|
+
|
94
|
+
client.delete_aggregation_schema(name)
|
95
|
+
|
96
|
+
$stderr.puts "Aggregation schema '#{name}' is deleted."
|
97
|
+
end
|
98
|
+
|
99
|
+
def aggr_add_log(op)
|
100
|
+
comment = nil
|
101
|
+
value_key = nil
|
102
|
+
count_key = nil
|
103
|
+
|
104
|
+
op.on('-m', '--comment COMMENT', 'comment of this entry') {|s|
|
105
|
+
comment = s
|
106
|
+
}
|
107
|
+
op.on('-v', '--value KEY_NAME', 'key name of value field') {|s|
|
108
|
+
value_key = s
|
109
|
+
}
|
110
|
+
op.on('-c', '--count KEY_NAME', 'key name of count field') {|s|
|
111
|
+
count_key = s
|
112
|
+
}
|
113
|
+
|
114
|
+
name, db_name, table_name, entry_name, o1_key, o2_key, o3_key = op.cmd_parse
|
115
|
+
|
116
|
+
okeys = [o1_key, o2_key, o3_key].compact
|
117
|
+
|
118
|
+
client = get_client
|
119
|
+
|
120
|
+
get_table(client, db_name, table_name)
|
121
|
+
|
122
|
+
begin
|
123
|
+
client.create_aggregation_log_entry(name, entry_name, comment, db_name, table_name, okeys, value_key, count_key)
|
124
|
+
rescue NotFoundError
|
125
|
+
cmd_debug_error $!
|
126
|
+
$stderr.puts "Aggregation schema '#{name}' does not exist."
|
127
|
+
$stderr.puts "Use '#{$prog} aggr:create #{name}' to create the aggregation schema."
|
128
|
+
exit 1
|
129
|
+
rescue AlreadyExistsError
|
130
|
+
$stderr.puts "Aggregation log entry '#{entry_name}' already exists."
|
131
|
+
exit 1
|
132
|
+
end
|
133
|
+
|
134
|
+
$stderr.puts "Aggregation log entry '#{entry_name}' is created."
|
135
|
+
end
|
136
|
+
|
137
|
+
def aggr_add_attr(op)
|
138
|
+
comment = nil
|
139
|
+
|
140
|
+
op.on('-m', '--comment COMMENT', 'comment of this entry') {|s|
|
141
|
+
comment = s
|
142
|
+
}
|
143
|
+
|
144
|
+
name, db_name, table_name, entry_name, method_name, *parameters = op.cmd_parse
|
145
|
+
|
146
|
+
params = {}
|
147
|
+
parameters.each {|pa|
|
148
|
+
k, v = pa.split('=')
|
149
|
+
params[k] = v
|
150
|
+
}
|
151
|
+
|
152
|
+
client = get_client
|
153
|
+
|
154
|
+
get_table(client, db_name, table_name)
|
155
|
+
|
156
|
+
begin
|
157
|
+
client.create_aggregation_attr_entry(name, entry_name, comment, db_name, table_name, method_name, params)
|
158
|
+
rescue NotFoundError
|
159
|
+
cmd_debug_error $!
|
160
|
+
$stderr.puts "Aggregation schema '#{name}' does not exist."
|
161
|
+
$stderr.puts "Use '#{$prog} aggr:create #{name}' to create the aggregation schema."
|
162
|
+
exit 1
|
163
|
+
rescue AlreadyExistsError
|
164
|
+
$stderr.puts "Aggregation attribute entry '#{entry_name}' already exists."
|
165
|
+
exit 1
|
166
|
+
end
|
167
|
+
|
168
|
+
$stderr.puts "Aggregation attribute entry '#{entry_name}' is created."
|
169
|
+
end
|
170
|
+
|
171
|
+
def aggr_del_log(op)
|
172
|
+
name, entry_name = op.cmd_parse
|
173
|
+
|
174
|
+
client = get_client
|
175
|
+
|
176
|
+
begin
|
177
|
+
client.delete_aggregation_log_entry(name, entry_name)
|
178
|
+
rescue NotFoundError
|
179
|
+
$stderr.puts "Aggregation log entry '#{entry_name}' does not exist."
|
180
|
+
exit 1
|
181
|
+
end
|
182
|
+
|
183
|
+
$stderr.puts "Aggregation log entry '#{entry_name}' is deleted."
|
184
|
+
end
|
185
|
+
|
186
|
+
def aggr_del_attr(op)
|
187
|
+
name, entry_name = op.cmd_parse
|
188
|
+
|
189
|
+
client = get_client
|
190
|
+
|
191
|
+
begin
|
192
|
+
client.delete_aggregation_attr_entry(name, entry_name)
|
193
|
+
rescue NotFoundError
|
194
|
+
$stderr.puts "Aggregation log entry '#{entry_name}' does not exist."
|
195
|
+
exit 1
|
196
|
+
end
|
197
|
+
|
198
|
+
$stderr.puts "Aggregation log entry '#{entry_name}' is deleted."
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
end
|
data/lib/td/command/common.rb
CHANGED
@@ -11,8 +11,8 @@ autoload :Job, 'td/client'
|
|
11
11
|
module Command
|
12
12
|
|
13
13
|
private
|
14
|
-
def
|
15
|
-
|
14
|
+
def initialize
|
15
|
+
@render_indent = ''
|
16
16
|
end
|
17
17
|
|
18
18
|
def get_client
|
@@ -28,10 +28,10 @@ module Command
|
|
28
28
|
Hirb::Helpers::Table.render(rows, *opts)
|
29
29
|
end
|
30
30
|
|
31
|
-
def cmd_render_tree(nodes, *opts)
|
32
|
-
|
33
|
-
|
34
|
-
end
|
31
|
+
#def cmd_render_tree(nodes, *opts)
|
32
|
+
# require 'hirb'
|
33
|
+
# Hirb::Helpers::Tree.render(nodes, *opts)
|
34
|
+
#end
|
35
35
|
|
36
36
|
def cmd_debug_error(ex)
|
37
37
|
if $verbose
|
@@ -43,6 +43,32 @@ module Command
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
def cmd_format_elapsed(start, finish)
|
47
|
+
if start
|
48
|
+
if !finish
|
49
|
+
finish = Time.now.utc
|
50
|
+
end
|
51
|
+
e = finish.to_i - start.to_i
|
52
|
+
elapsed = ''
|
53
|
+
if e >= 3600
|
54
|
+
elapsed << "#{e/3600}h "
|
55
|
+
e %= 3600
|
56
|
+
elapsed << "% 2dm " % (e/60)
|
57
|
+
e %= 60
|
58
|
+
elapsed << "% 2dsec" % e
|
59
|
+
elsif e >= 60
|
60
|
+
elapsed << "% 2dm " % (e/60)
|
61
|
+
e %= 60
|
62
|
+
elapsed << "% 2dsec" % e
|
63
|
+
else
|
64
|
+
elapsed << "% 2dsec" % e
|
65
|
+
end
|
66
|
+
else
|
67
|
+
elapsed = ''
|
68
|
+
end
|
69
|
+
elapsed = "% 10s" % elapsed # right aligned
|
70
|
+
end
|
71
|
+
|
46
72
|
def get_database(client, db_name)
|
47
73
|
begin
|
48
74
|
return client.database(db_name)
|
data/lib/td/command/help.rb
CHANGED
@@ -5,14 +5,22 @@ module Command
|
|
5
5
|
def help(op)
|
6
6
|
cmd = op.cmd_parse
|
7
7
|
|
8
|
-
|
9
|
-
unless
|
8
|
+
usage = List.cmd_usage(cmd)
|
9
|
+
unless usage
|
10
10
|
$stderr.puts "'#{cmd}' is not a td command. Run '#{$prog}' to show the list."
|
11
11
|
List.show_guess(cmd)
|
12
12
|
exit 1
|
13
13
|
end
|
14
14
|
|
15
|
-
puts
|
15
|
+
puts usage
|
16
|
+
end
|
17
|
+
|
18
|
+
def help_all(op)
|
19
|
+
cmd = op.cmd_parse
|
20
|
+
|
21
|
+
TreasureData::Command::List.show_help(op.summary_indent)
|
22
|
+
puts ""
|
23
|
+
puts "Type '#{$prog} help COMMAND' for more information on a specific command."
|
16
24
|
end
|
17
25
|
|
18
26
|
end
|
data/lib/td/command/import.rb
CHANGED
@@ -222,6 +222,10 @@ module Command
|
|
222
222
|
begin
|
223
223
|
record = JSON.parse(line)
|
224
224
|
|
225
|
+
unless record.is_a?(Hash)
|
226
|
+
raise "record must be a Hash"
|
227
|
+
end
|
228
|
+
|
225
229
|
time = record[@time_key]
|
226
230
|
unless time
|
227
231
|
raise "record doesn't have '#{@time_key}' column"
|
@@ -255,6 +259,10 @@ module Command
|
|
255
259
|
MessagePack::Unpacker.new(file).each {|record|
|
256
260
|
i += 1
|
257
261
|
begin
|
262
|
+
unless record.is_a?(Hash)
|
263
|
+
raise "record must be a Hash"
|
264
|
+
end
|
265
|
+
|
258
266
|
time = record[@time_key]
|
259
267
|
unless time
|
260
268
|
raise "record doesn't have '#{@time_key}' column"
|
data/lib/td/command/job.rb
CHANGED
@@ -5,6 +5,8 @@ module Command
|
|
5
5
|
def job_list(op)
|
6
6
|
page = 0
|
7
7
|
skip = 0
|
8
|
+
running = false
|
9
|
+
error = false
|
8
10
|
|
9
11
|
op.on('-p', '--page PAGE', 'skip N pages', Integer) {|i|
|
10
12
|
page = i
|
@@ -12,6 +14,12 @@ module Command
|
|
12
14
|
op.on('-s', '--skip N', 'skip N jobs', Integer) {|i|
|
13
15
|
skip = i
|
14
16
|
}
|
17
|
+
op.on('-R', '--running', 'show only running jobs', TrueClass) {|b|
|
18
|
+
running = b
|
19
|
+
}
|
20
|
+
op.on('-E', '--error', 'show only error jobs', TrueClass) {|b|
|
21
|
+
error = b
|
22
|
+
}
|
15
23
|
|
16
24
|
max = op.cmd_parse
|
17
25
|
|
@@ -26,36 +34,14 @@ module Command
|
|
26
34
|
|
27
35
|
rows = []
|
28
36
|
jobs.each {|job|
|
37
|
+
next if running && !job.running?
|
38
|
+
next if error && !job.error?
|
29
39
|
start = job.start_at
|
30
|
-
|
31
|
-
|
32
|
-
if !finish
|
33
|
-
finish = Time.now.utc
|
34
|
-
end
|
35
|
-
e = finish.to_i - start.to_i
|
36
|
-
elapsed = ''
|
37
|
-
if e >= 3600
|
38
|
-
elapsed << "#{e/3600}h "
|
39
|
-
e %= 3600
|
40
|
-
elapsed << "% 2dm " % (e/60)
|
41
|
-
e %= 60
|
42
|
-
elapsed << "% 2dsec" % e
|
43
|
-
elsif e >= 60
|
44
|
-
elapsed << "% 2dm " % (e/60)
|
45
|
-
e %= 60
|
46
|
-
elapsed << "% 2dsec" % e
|
47
|
-
else
|
48
|
-
elapsed << "% 2dsec" % e
|
49
|
-
end
|
50
|
-
else
|
51
|
-
elapsed = ''
|
52
|
-
end
|
53
|
-
elapsed = "% 10s" % elapsed # right aligned
|
54
|
-
|
55
|
-
rows << {:JobID => job.job_id, :Status => job.status, :Query => job.query.to_s, :Start => (start ? start.localtime : ''), :Elapsed => elapsed}
|
40
|
+
elapsed = cmd_format_elapsed(start, job.end_at)
|
41
|
+
rows << {:JobID => job.job_id, :Status => job.status, :Query => job.query.to_s, :Start => (start ? start.localtime : ''), :Elapsed => elapsed, :Result => job.rset_name}
|
56
42
|
}
|
57
43
|
|
58
|
-
puts cmd_render_table(rows, :fields => [:JobID, :Status, :Start, :Elapsed, :Query])
|
44
|
+
puts cmd_render_table(rows, :fields => [:JobID, :Status, :Start, :Elapsed, :Result, :Query])
|
59
45
|
end
|
60
46
|
|
61
47
|
def job_show(op)
|
@@ -86,21 +72,22 @@ module Command
|
|
86
72
|
|
87
73
|
job = client.job(job_id)
|
88
74
|
|
89
|
-
puts "JobID
|
90
|
-
puts "URL
|
91
|
-
puts "Status
|
92
|
-
puts "Query
|
75
|
+
puts "JobID : #{job.job_id}"
|
76
|
+
puts "URL : #{job.url}"
|
77
|
+
puts "Status : #{job.status}"
|
78
|
+
puts "Query : #{job.query}"
|
79
|
+
puts "Result table : #{job.rset_name}"
|
93
80
|
|
94
81
|
if wait && !job.finished?
|
95
82
|
wait_job(job)
|
96
83
|
if job.success?
|
97
|
-
puts "Result
|
84
|
+
puts "Result :"
|
98
85
|
show_result(job, output, format)
|
99
86
|
end
|
100
87
|
|
101
88
|
else
|
102
89
|
if job.success?
|
103
|
-
puts "Result
|
90
|
+
puts "Result :"
|
104
91
|
show_result(job, output, format)
|
105
92
|
end
|
106
93
|
|
@@ -231,7 +218,8 @@ module Command
|
|
231
218
|
# TODO limit number of rows to show
|
232
219
|
rows << row.map {|v|
|
233
220
|
if v.is_a?(String)
|
234
|
-
|
221
|
+
# TODO encoding check
|
222
|
+
v.to_s.force_encoding('ASCII-8BIT')
|
235
223
|
else
|
236
224
|
v.to_json
|
237
225
|
end
|
data/lib/td/command/list.rb
CHANGED
@@ -3,69 +3,36 @@ module TreasureData
|
|
3
3
|
module Command
|
4
4
|
module List
|
5
5
|
|
6
|
-
class
|
7
|
-
def initialize(name,
|
6
|
+
class CommandParser < OptionParser
|
7
|
+
def initialize(name, req_args, opt_args, varlen, argv)
|
8
8
|
super()
|
9
|
-
|
10
|
-
@
|
11
|
-
@
|
12
|
-
|
13
|
-
if args.last.to_s =~ /_$/
|
14
|
-
@varlen = true
|
15
|
-
args.push args.pop.to_s[0..-2]+'...'
|
16
|
-
elsif args.last.to_s =~ /_\?$/
|
17
|
-
@varlen = true
|
18
|
-
args.push args.pop.to_s[0..-3]+'...?'
|
19
|
-
end
|
20
|
-
|
21
|
-
@req_args, opt_args = args.partition {|a| a.to_s !~ /\?$/ }
|
22
|
-
@opt_args = opt_args.map {|a| a.to_s[0..-2].to_sym }
|
23
|
-
@args = @req_args + @opt_args
|
24
|
-
|
25
|
-
@usage_args = "#{@name}"
|
26
|
-
@req_args.each {|a| @usage_args << " <#{a}>" }
|
27
|
-
@opt_args.each {|a| @usage_args << " [#{a}]" }
|
28
|
-
|
9
|
+
@req_args = req_args
|
10
|
+
@opt_args = opt_args
|
11
|
+
@varlen = varlen
|
12
|
+
@argv = argv
|
29
13
|
@has_options = false
|
30
|
-
|
31
|
-
self.summary_indent = " "
|
32
|
-
|
33
|
-
banner = "usage:\n"
|
34
|
-
banner << " $ #{File.basename($0)} #{@usage_args}\n"
|
35
|
-
banner << "\n"
|
36
|
-
banner << "description:\n"
|
37
|
-
@description.split("\n").each {|l|
|
38
|
-
banner << " #{l}\n"
|
39
|
-
}
|
40
|
-
self.banner = banner
|
41
|
-
|
42
|
-
@message = nil
|
43
|
-
@argv = nil
|
14
|
+
@message = ''
|
44
15
|
end
|
45
16
|
|
46
|
-
attr_accessor :message
|
17
|
+
attr_accessor :message
|
47
18
|
|
48
|
-
def
|
49
|
-
@
|
19
|
+
def on(*argv)
|
20
|
+
@has_options = true
|
21
|
+
super
|
50
22
|
end
|
51
23
|
|
52
24
|
def banner
|
53
|
-
s =
|
25
|
+
s = @message.dup
|
54
26
|
if @has_options
|
55
27
|
s << "\n"
|
56
28
|
s << "options:\n"
|
57
29
|
end
|
58
|
-
s << "\n"
|
59
30
|
s
|
60
31
|
end
|
61
32
|
|
62
|
-
def usage
|
63
|
-
"%-40s # %s" % [@usage_args, @description]
|
64
|
-
end
|
65
|
-
|
66
33
|
def cmd_parse(argv=@argv||ARGV)
|
67
34
|
parse!(argv)
|
68
|
-
if argv.length < @req_args.length || (!@varlen && argv.length > @
|
35
|
+
if argv.length < @req_args.length || (!@varlen && argv.length > (@req_args.length+@opt_args.length))
|
69
36
|
cmd_usage nil
|
70
37
|
end
|
71
38
|
if argv.length <= 1
|
@@ -78,40 +45,92 @@ module List
|
|
78
45
|
end
|
79
46
|
|
80
47
|
def cmd_usage(msg=nil)
|
81
|
-
puts self.
|
82
|
-
if msg
|
83
|
-
puts ""
|
84
|
-
puts "error: #{msg}"
|
85
|
-
end
|
48
|
+
puts self.to_s
|
49
|
+
puts "error: #{msg}" if msg
|
86
50
|
exit 1
|
87
51
|
end
|
52
|
+
end
|
88
53
|
|
89
|
-
|
90
|
-
|
54
|
+
class CommandOption
|
55
|
+
def initialize(name, args, description, examples)
|
56
|
+
@name = name
|
57
|
+
@args = args
|
58
|
+
@description = description.to_s
|
59
|
+
@examples = examples
|
60
|
+
@override_message = nil
|
91
61
|
end
|
92
62
|
|
93
|
-
|
94
|
-
|
95
|
-
|
63
|
+
attr_reader :name, :args, :description, :examples
|
64
|
+
attr_accessor :override_message
|
65
|
+
|
66
|
+
def compile!
|
67
|
+
return if @usage_args
|
68
|
+
|
69
|
+
args = @args.dup
|
70
|
+
if args.last.to_s =~ /_$/
|
71
|
+
@varlen = true
|
72
|
+
args.push args.pop.to_s[0..-2]+'...'
|
73
|
+
elsif args.last.to_s =~ /_\?$/
|
74
|
+
@varlen = true
|
75
|
+
args.push args.pop.to_s[0..-3]+'...?'
|
76
|
+
end
|
77
|
+
|
78
|
+
@req_args, @opt_args = args.partition {|a| a.to_s !~ /\?$/ }
|
79
|
+
@opt_args = @opt_args.map {|a| a.to_s[0..-2].to_sym }
|
80
|
+
|
81
|
+
@usage_args = "#{@name}"
|
82
|
+
@req_args.each {|a| @usage_args << " <#{a}>" }
|
83
|
+
@opt_args.each {|a| @usage_args << " [#{a}]" }
|
96
84
|
end
|
97
85
|
|
98
|
-
def
|
99
|
-
|
100
|
-
|
101
|
-
|
86
|
+
def create_optparse(argv)
|
87
|
+
compile!
|
88
|
+
op = CommandParser.new(@name, @req_args, @opt_args, @varlen, argv)
|
89
|
+
|
90
|
+
message = "usage:\n"
|
91
|
+
message << " $ #{File.basename($0)} #{@usage_args}\n"
|
92
|
+
unless @examples.empty?
|
93
|
+
message << "\n"
|
94
|
+
message << "example:\n"
|
95
|
+
@examples.each {|l|
|
96
|
+
message << " $ #{File.basename($0)} #{l}\n"
|
97
|
+
}
|
98
|
+
end
|
99
|
+
message << "\n"
|
100
|
+
message << "description:\n"
|
101
|
+
@description.split("\n").each {|l|
|
102
|
+
message << " #{l}\n"
|
103
|
+
}
|
104
|
+
|
105
|
+
op.message = message
|
106
|
+
op.summary_indent = " "
|
107
|
+
|
108
|
+
if msg = @override_message
|
109
|
+
(class<<op;self;end).module_eval do
|
110
|
+
define_method(:to_s) { msg }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
op
|
115
|
+
end
|
116
|
+
|
117
|
+
def usage
|
118
|
+
compile!
|
119
|
+
"%-40s # %s" % [@usage_args, @description]
|
102
120
|
end
|
103
121
|
|
104
|
-
|
105
|
-
|
122
|
+
def group
|
123
|
+
@name.split(':', 2).first
|
124
|
+
end
|
106
125
|
end
|
107
126
|
|
108
127
|
LIST = []
|
109
128
|
COMMAND = {}
|
110
129
|
GUESS = {}
|
111
|
-
HELP_EXCLUDE = [
|
130
|
+
HELP_EXCLUDE = [/^help/, /^account/, /^aggr/]
|
112
131
|
|
113
|
-
def self.add_list(name, args, description)
|
114
|
-
LIST << COMMAND[name] = CommandOption.new(name, args, description)
|
132
|
+
def self.add_list(name, args, description, *examples)
|
133
|
+
LIST << COMMAND[name] = CommandOption.new(name, args, description, examples)
|
115
134
|
end
|
116
135
|
|
117
136
|
def self.add_alias(new_cmd, old_cmd)
|
@@ -122,15 +141,22 @@ module List
|
|
122
141
|
GUESS[wrong] = correct
|
123
142
|
end
|
124
143
|
|
144
|
+
def self.cmd_usage(name)
|
145
|
+
if c = COMMAND[name]
|
146
|
+
c.create_optparse([]).cmd_usage
|
147
|
+
end
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
|
125
151
|
def self.get_method(name)
|
126
|
-
if
|
127
|
-
name =
|
128
|
-
group, action =
|
152
|
+
if c = COMMAND[name]
|
153
|
+
name = c.name
|
154
|
+
group, action = c.group
|
129
155
|
require 'td/command/common'
|
130
156
|
require "td/command/#{group}"
|
131
|
-
cmd = name.gsub(
|
157
|
+
cmd = name.gsub(/[\:\-]/, '_')
|
132
158
|
m = Object.new.extend(Command).method(cmd)
|
133
|
-
return Proc.new {|args| m.call(
|
159
|
+
return Proc.new {|args| m.call(c.create_optparse(args)) }
|
134
160
|
end
|
135
161
|
nil
|
136
162
|
end
|
@@ -147,74 +173,96 @@ module List
|
|
147
173
|
|
148
174
|
def self.show_help(indent=' ')
|
149
175
|
before_group = nil
|
150
|
-
LIST.each {|
|
151
|
-
next if HELP_EXCLUDE.
|
152
|
-
if before_group !=
|
153
|
-
before_group =
|
176
|
+
LIST.each {|c|
|
177
|
+
next if HELP_EXCLUDE.any? {|pattern| pattern =~ c.name }
|
178
|
+
if before_group != c.group
|
179
|
+
before_group = c.group
|
154
180
|
puts ""
|
155
181
|
end
|
156
|
-
puts "#{indent}#{
|
182
|
+
puts "#{indent}#{c.usage}"
|
157
183
|
}
|
158
184
|
end
|
159
185
|
|
160
186
|
def self.get_group(group)
|
161
|
-
LIST.map {|
|
162
|
-
|
187
|
+
LIST.map {|c|
|
188
|
+
c.group == group
|
163
189
|
}
|
164
190
|
end
|
165
191
|
|
166
192
|
def self.finishup
|
167
193
|
groups = {}
|
168
|
-
LIST.each {|
|
169
|
-
(groups[
|
194
|
+
LIST.each {|c|
|
195
|
+
(groups[c.group] ||= []) << c
|
170
196
|
}
|
171
197
|
groups.each_pair {|group,ops|
|
172
|
-
if ops.size > 1 &&
|
198
|
+
if ops.size > 1 && c = COMMAND[group]
|
199
|
+
c = c.dup
|
200
|
+
|
173
201
|
msg = %[Additional commands, type "#{File.basename($0)} help COMMAND" for more details:\n\n]
|
174
|
-
ops.each {|
|
175
|
-
msg << %[ #{
|
202
|
+
ops.each {|c|
|
203
|
+
msg << %[ #{c.usage}\n]
|
176
204
|
}
|
177
205
|
msg << %[\n]
|
178
|
-
|
179
|
-
|
206
|
+
c.override_message = msg
|
207
|
+
|
208
|
+
COMMAND[group] = c
|
180
209
|
end
|
181
210
|
}
|
182
211
|
end
|
183
212
|
|
184
|
-
add_list 'db:list', %w[], 'Show list of tables in a database'
|
185
|
-
add_list 'db:show', %w[db], 'Describe a information of a database'
|
186
|
-
add_list 'db:create', %w[db], 'Create a database'
|
187
|
-
add_list 'db:delete', %w[db], 'Delete a database'
|
213
|
+
add_list 'db:list', %w[], 'Show list of tables in a database', 'db:list', 'dbs'
|
214
|
+
add_list 'db:show', %w[db], 'Describe a information of a database', 'db example_db'
|
215
|
+
add_list 'db:create', %w[db], 'Create a database', 'db:create example_db'
|
216
|
+
add_list 'db:delete', %w[db], 'Delete a database', 'db:delete example_db'
|
217
|
+
|
218
|
+
add_list 'table:list', %w[db?], 'Show list of tables', 'table:list', 'table:list example_db', 'tables'
|
219
|
+
add_list 'table:show', %w[db table], 'Describe a information of a table', 'table example_db table1'
|
220
|
+
add_list 'table:create', %w[db table], 'Create a table', 'table:create example_db table1'
|
221
|
+
add_list 'table:delete', %w[db table], 'Delete a table', 'table:delete example_db table1'
|
222
|
+
add_list 'table:import', %w[db table files_], 'Parse and import files to a table', 'table:import example_db table1 --apache access.log', 'table:import example_db table1 --json -t time - < test.json'
|
223
|
+
add_list 'table:tail', %w[db table], 'Get recently imported logs', 'table:tail example_db table1', 'table:tail example_db table1 -t "2011-01-02 03:04:05" -n 30'
|
224
|
+
|
225
|
+
add_list 'result:info', %w[], 'Show information of the MySQL server', 'result:info'
|
226
|
+
add_list 'result:list', %w[], 'Show list of result tables', 'result:list', 'results'
|
227
|
+
add_list 'result:create', %w[name], 'Create a result table', 'result:create rset1'
|
228
|
+
add_list 'result:delete', %w[name], 'Delete a result table', 'result:delete rset1'
|
229
|
+
add_list 'result:connect', %w[sql?], 'Connect to the server using mysql command', 'result:connect'
|
230
|
+
#add_list 'result:get', %w[name], 'Download dump of the result table'
|
188
231
|
|
189
|
-
add_list '
|
190
|
-
add_list 'table:show', %w[db table], 'Describe a information of a table'
|
191
|
-
add_list 'table:create', %w[db table], 'Create a table'
|
192
|
-
add_list 'table:delete', %w[db table], 'Delete a table'
|
193
|
-
add_list 'table:import', %w[db table files_], 'Parse and import files to a table'
|
194
|
-
add_list 'table:tail', %w[db table], 'Get recently imported logs'
|
232
|
+
add_list 'status', %w[], 'Show schedules, jobs, tables and results', 'status', 's'
|
195
233
|
|
196
|
-
add_list 'schema:show', %w[db table], 'Show schema of a table'
|
197
|
-
add_list 'schema:set', %w[db table columns_?], 'Set new schema on a table'
|
198
|
-
add_list 'schema:add', %w[db table columns_], 'Add new columns to a table'
|
199
|
-
add_list 'schema:remove', %w[db table columns_], 'Remove columns from a table'
|
234
|
+
add_list 'schema:show', %w[db table], 'Show schema of a table', 'schema example_db table1'
|
235
|
+
add_list 'schema:set', %w[db table columns_?], 'Set new schema on a table', 'schema:set example_db table1 user:string size:int'
|
236
|
+
add_list 'schema:add', %w[db table columns_], 'Add new columns to a table', 'schema:add example_db table1 user:string size:int'
|
237
|
+
add_list 'schema:remove', %w[db table columns_], 'Remove columns from a table', 'schema:remove example_db table1 user size'
|
200
238
|
|
201
|
-
add_list 'sched:list', %w[], 'Show list of schedules'
|
202
|
-
add_list 'sched:create', %w[name cron sql], 'Create a schedule'
|
203
|
-
add_list 'sched:delete', %w[name], 'Delete a schedule'
|
204
|
-
add_list 'sched:history', %w[name max?], 'Show history of scheduled queries'
|
239
|
+
add_list 'sched:list', %w[], 'Show list of schedules', 'sched:list', 'scheds'
|
240
|
+
add_list 'sched:create', %w[name cron sql], 'Create a schedule', 'sched:create sched1 "0 * * * *" -d example_db "select count(*) from table1" -r rset1'
|
241
|
+
add_list 'sched:delete', %w[name], 'Delete a schedule', 'sched:delete sched1'
|
242
|
+
add_list 'sched:history', %w[name max?], 'Show history of scheduled queries', 'sched sched1 --page 1'
|
205
243
|
|
206
|
-
add_list 'query', %w[sql], 'Issue a query'
|
244
|
+
add_list 'query', %w[sql], 'Issue a query', 'query -d example_db -w -r rset1 "select count(*) from table1"'
|
207
245
|
|
208
|
-
add_list 'job:show', %w[job_id], 'Show status and result of a job'
|
209
|
-
add_list 'job:list', %w[max?], 'Show list of jobs'
|
210
|
-
add_list 'job:kill', %w[job_id], 'Kill or cancel a job'
|
246
|
+
add_list 'job:show', %w[job_id], 'Show status and result of a job', 'job 1461'
|
247
|
+
add_list 'job:list', %w[max?], 'Show list of jobs', 'jobs', 'jobs --page 1'
|
248
|
+
add_list 'job:kill', %w[job_id], 'Kill or cancel a job', 'job:kill 1461'
|
211
249
|
|
212
250
|
add_list 'account', %w[user_name?], 'Setup a Treasure Data account'
|
213
251
|
add_list 'apikey:show', %w[], 'Show Treasure Data API key'
|
214
252
|
add_list 'apikey:set', %w[apikey], 'Set Treasure Data API key'
|
215
253
|
|
254
|
+
add_list 'aggr:list', %w[], 'Show list of aggregation schemas'
|
255
|
+
add_list 'aggr:show', %w[name], 'Describe a aggregation schema'
|
256
|
+
add_list 'aggr:create', %w[name relation_key], 'Create a aggregation schema'
|
257
|
+
add_list 'aggr:delete', %w[name], 'Delete a aggregation schema'
|
258
|
+
add_list 'aggr:add-log', %w[name db table entry_name o1_key? o2_key? o3_key?], 'Add a log aggregation entry'
|
259
|
+
add_list 'aggr:add-attr', %w[name db table entry_name method_name parameters_?], 'Add an attribute aggregation entry'
|
260
|
+
add_list 'aggr:del-log', %w[name entry_name], 'Delete a log aggregation entry'
|
261
|
+
add_list 'aggr:del-attr', %w[name entry_name], 'Delete an attribute aggregation entry'
|
262
|
+
|
216
263
|
add_list 'server:status', %w[], 'Show status of the Treasure Data server'
|
217
264
|
|
265
|
+
add_list 'help:all', %w[], 'Show usage of all commands'
|
218
266
|
add_list 'help', %w[command], 'Show usage of a command'
|
219
267
|
|
220
268
|
# aliases
|
@@ -231,6 +279,9 @@ module List
|
|
231
279
|
add_alias 'table', 'table:show'
|
232
280
|
add_alias 'tables', 'table:list'
|
233
281
|
|
282
|
+
add_alias 'result', 'help' # dummy
|
283
|
+
add_alias 'results', 'result:list'
|
284
|
+
|
234
285
|
add_alias 'schema', 'schema:show'
|
235
286
|
|
236
287
|
add_alias 'schedule:list', 'sched:list'
|
@@ -247,7 +298,13 @@ module List
|
|
247
298
|
add_alias 'jobs', 'job:list'
|
248
299
|
add_alias 'kill', 'job:kill'
|
249
300
|
|
301
|
+
add_alias 'aggr', 'aggr:show'
|
302
|
+
add_alias 'aggrs', 'aggr:list'
|
303
|
+
|
250
304
|
add_alias 'apikey', 'apikey:show'
|
305
|
+
add_alias 'server', 'server:status'
|
306
|
+
|
307
|
+
add_alias 's', 'status'
|
251
308
|
|
252
309
|
# backward compatibility
|
253
310
|
add_alias 'show-databases', 'db:list'
|
data/lib/td/command/query.rb
CHANGED
@@ -7,6 +7,7 @@ module Command
|
|
7
7
|
wait = false
|
8
8
|
output = nil
|
9
9
|
format = 'tsv'
|
10
|
+
result = nil
|
10
11
|
|
11
12
|
op.on('-d', '--database DB_NAME', 'use the database (required)') {|s|
|
12
13
|
db_name = s
|
@@ -14,6 +15,9 @@ module Command
|
|
14
15
|
op.on('-w', '--wait', 'wait for finishing the job', TrueClass) {|b|
|
15
16
|
wait = b
|
16
17
|
}
|
18
|
+
op.on('-r', '--result RESULT_TABLE', 'write result to the result table (use result:create command)') {|s|
|
19
|
+
result = s
|
20
|
+
}
|
17
21
|
op.on('-o', '--output PATH', 'write result to the file') {|s|
|
18
22
|
output = s
|
19
23
|
}
|
@@ -36,7 +40,7 @@ module Command
|
|
36
40
|
# local existance check
|
37
41
|
get_database(client, db_name)
|
38
42
|
|
39
|
-
job = client.query(db_name, sql)
|
43
|
+
job = client.query(db_name, sql, result)
|
40
44
|
|
41
45
|
$stderr.puts "Job #{job.job_id} is queued."
|
42
46
|
$stderr.puts "Use '#{$prog} job:show #{job.job_id}' to show the status."
|
@@ -0,0 +1,111 @@
|
|
1
|
+
|
2
|
+
module TreasureData
|
3
|
+
module Command
|
4
|
+
|
5
|
+
def result_info(op)
|
6
|
+
op.cmd_parse
|
7
|
+
|
8
|
+
client = get_client
|
9
|
+
|
10
|
+
info = client.result_set_info
|
11
|
+
|
12
|
+
puts "Type : #{info.type}"
|
13
|
+
puts "Host : #{info.host}"
|
14
|
+
puts "Port : #{info.port}"
|
15
|
+
puts "User : #{info.user}"
|
16
|
+
puts "Password : #{info.password}"
|
17
|
+
puts "Database : #{info.database}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def result_list(op)
|
21
|
+
op.cmd_parse
|
22
|
+
|
23
|
+
client = get_client
|
24
|
+
|
25
|
+
rsets = client.result_sets
|
26
|
+
|
27
|
+
rows = []
|
28
|
+
rsets.each {|rset|
|
29
|
+
rows << {:Name => rset.name}
|
30
|
+
}
|
31
|
+
rows = rows.sort_by {|map|
|
32
|
+
map[:Name]
|
33
|
+
}
|
34
|
+
|
35
|
+
puts cmd_render_table(rows, :fields => [:Name])
|
36
|
+
|
37
|
+
if rsets.empty?
|
38
|
+
$stderr.puts "There are result tables."
|
39
|
+
$stderr.puts "Use '#{$prog} result:create <name>' to create a result table."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def result_create(op)
|
44
|
+
name = op.cmd_parse
|
45
|
+
|
46
|
+
API.validate_database_name(name)
|
47
|
+
|
48
|
+
client = get_client
|
49
|
+
|
50
|
+
begin
|
51
|
+
client.create_result_set(name)
|
52
|
+
rescue AlreadyExistsError
|
53
|
+
$stderr.puts "Result table '#{name}' already exists."
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
|
57
|
+
$stderr.puts "Result table '#{name}' is created."
|
58
|
+
end
|
59
|
+
|
60
|
+
def result_delete(op)
|
61
|
+
name = op.cmd_parse
|
62
|
+
|
63
|
+
client = get_client
|
64
|
+
|
65
|
+
begin
|
66
|
+
client.delete_result_set(name)
|
67
|
+
rescue NotFoundError
|
68
|
+
$stderr.puts "Result table '#{name}' does not exist."
|
69
|
+
exit 1
|
70
|
+
end
|
71
|
+
|
72
|
+
$stderr.puts "Result table '#{name}' is deleted."
|
73
|
+
end
|
74
|
+
|
75
|
+
def result_connect(op)
|
76
|
+
mysql = 'mysql'
|
77
|
+
|
78
|
+
op.on('-e', '--execute MYSQL', 'mysql command') {|s|
|
79
|
+
mysql = s
|
80
|
+
}
|
81
|
+
|
82
|
+
sql = op.cmd_parse
|
83
|
+
|
84
|
+
client = get_client
|
85
|
+
|
86
|
+
info = client.result_set_info
|
87
|
+
|
88
|
+
cmd = [mysql, '-h', info.host, '-P', info.port.to_s, '-u', info.user, "--password=#{info.password}", info.database]
|
89
|
+
|
90
|
+
cmd_start = Time.now
|
91
|
+
|
92
|
+
if sql
|
93
|
+
IO.popen(cmd, "w") {|io|
|
94
|
+
io.write sql
|
95
|
+
io.close
|
96
|
+
}
|
97
|
+
else
|
98
|
+
STDERR.puts "> #{cmd.join(' ')}"
|
99
|
+
system(*cmd)
|
100
|
+
end
|
101
|
+
|
102
|
+
cmd_alive = Time.now - cmd_start
|
103
|
+
if $?.to_i != 0 && cmd_alive < 1.0
|
104
|
+
STDERR.puts "Command died within 1 second with exit code #{$?.to_i}."
|
105
|
+
STDERR.puts "Please confirm mysql command is installed."
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
data/lib/td/command/runner.rb
CHANGED
@@ -33,10 +33,26 @@ EOF
|
|
33
33
|
require 'td/command/list'
|
34
34
|
puts op.to_s
|
35
35
|
puts ""
|
36
|
-
puts
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
puts <<EOF
|
37
|
+
Basic commands:
|
38
|
+
|
39
|
+
db # create/delete/list databases
|
40
|
+
table # create/delete/list/import/tail tables
|
41
|
+
query # issue a query
|
42
|
+
job # show/kill/list jobs
|
43
|
+
result # create/delete/list/connect/get MySQL result set
|
44
|
+
|
45
|
+
Additional commands:
|
46
|
+
|
47
|
+
sched # create/delete/list schedules that run a query periodically
|
48
|
+
schema # create/delete/modify schemas of tables
|
49
|
+
status # show scheds, jobs, tables and results
|
50
|
+
apikey # show/set API key
|
51
|
+
server # show status of the Treasure Data server
|
52
|
+
help # show help messages
|
53
|
+
|
54
|
+
Type 'td help COMMAND' for more information on a specific command.
|
55
|
+
EOF
|
40
56
|
if errmsg
|
41
57
|
puts "error: #{errmsg}"
|
42
58
|
exit 1
|
data/lib/td/command/sched.rb
CHANGED
@@ -11,21 +11,25 @@ module Command
|
|
11
11
|
|
12
12
|
rows = []
|
13
13
|
scheds.each {|sched|
|
14
|
-
rows << {:Name => sched.name, :Cron => sched.cron, :Query => sched.query}
|
14
|
+
rows << {:Name => sched.name, :Cron => sched.cron, :Result => sched.rset_name, :Query => sched.query}
|
15
15
|
}
|
16
16
|
rows = rows.sort_by {|map|
|
17
17
|
map[:Name]
|
18
18
|
}
|
19
19
|
|
20
|
-
puts cmd_render_table(rows, :fields => [:Name, :Cron, :Query])
|
20
|
+
puts cmd_render_table(rows, :fields => [:Name, :Cron, :Result, :Query])
|
21
21
|
end
|
22
22
|
|
23
23
|
def sched_create(op)
|
24
24
|
db_name = nil
|
25
|
+
result = nil
|
25
26
|
|
26
27
|
op.on('-d', '--database DB_NAME', 'use the database (required)') {|s|
|
27
28
|
db_name = s
|
28
29
|
}
|
30
|
+
op.on('-r', '--result RESULT_TABLE', 'write result to the result table (use result:create command)') {|s|
|
31
|
+
result = s
|
32
|
+
}
|
29
33
|
|
30
34
|
name, cron, sql = op.cmd_parse
|
31
35
|
|
@@ -40,7 +44,7 @@ module Command
|
|
40
44
|
get_database(client, db_name)
|
41
45
|
|
42
46
|
begin
|
43
|
-
first_time = client.create_schedule(name, :cron=>cron, :query=>sql, :database=>db_name)
|
47
|
+
first_time = client.create_schedule(name, :cron=>cron, :query=>sql, :database=>db_name, :result=>result)
|
44
48
|
rescue AlreadyExistsError
|
45
49
|
cmd_debug_error $!
|
46
50
|
$stderr.puts "Schedule '#{name}' already exists."
|
@@ -99,10 +103,10 @@ module Command
|
|
99
103
|
|
100
104
|
rows = []
|
101
105
|
history.each {|j|
|
102
|
-
rows << {:Time => j.scheduled_at.localtime, :JobID => j.job_id, :Status => j.status}
|
106
|
+
rows << {:Time => j.scheduled_at.localtime, :JobID => j.job_id, :Status => j.status, :Result=>j.rset_name}
|
103
107
|
}
|
104
108
|
|
105
|
-
puts cmd_render_table(rows, :fields => [:JobID, :Time, :Status])
|
109
|
+
puts cmd_render_table(rows, :fields => [:JobID, :Time, :Status, :Result])
|
106
110
|
end
|
107
111
|
|
108
112
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
|
2
|
+
module TreasureData
|
3
|
+
module Command
|
4
|
+
|
5
|
+
def status(op)
|
6
|
+
op.cmd_parse
|
7
|
+
|
8
|
+
client = get_client
|
9
|
+
|
10
|
+
# +----------------+
|
11
|
+
# | scheds |
|
12
|
+
# +----------------+
|
13
|
+
# +----------------+
|
14
|
+
# | jobs |
|
15
|
+
# +----------------+
|
16
|
+
# +------+ +-------+
|
17
|
+
# |tables| |results|
|
18
|
+
# +------+ +-------+
|
19
|
+
|
20
|
+
scheds = []
|
21
|
+
jobs = []
|
22
|
+
tables = []
|
23
|
+
results = []
|
24
|
+
|
25
|
+
s = client.schedules
|
26
|
+
s.each {|sched|
|
27
|
+
scheds << {:Name => sched.name, :Cron => sched.cron, :Result => sched.rset_name, :Query => sched.query}
|
28
|
+
}
|
29
|
+
scheds = scheds.sort_by {|map|
|
30
|
+
map[:Name]
|
31
|
+
}
|
32
|
+
x1, y1 = status_render(0, 0, "[Schedules]", scheds, :fields => [:Name, :Cron, :Result, :Query])
|
33
|
+
|
34
|
+
j = client.jobs(0, 4)
|
35
|
+
j.each {|job|
|
36
|
+
start = job.start_at
|
37
|
+
elapsed = cmd_format_elapsed(start, job.end_at)
|
38
|
+
jobs << {:JobID => job.job_id, :Status => job.status, :Query => job.query.to_s, :Start => (start ? start.localtime : ''), :Elapsed => elapsed, :Result => job.rset_name}
|
39
|
+
}
|
40
|
+
x2, y2 = status_render(0, 0, "[Jobs]", jobs, :fields => [:JobID, :Status, :Start, :Elapsed, :Result, :Query])
|
41
|
+
|
42
|
+
dbs = client.databases
|
43
|
+
dbs.map {|db|
|
44
|
+
db.tables.each {|table|
|
45
|
+
tables << {:Database => db.name, :Table => table.name, :Count => table.count.to_s}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
x3, y3 = status_render(0, 0, "[Tables]", tables, :fields => [:Database, :Table, :Count])
|
49
|
+
|
50
|
+
r = client.result_sets
|
51
|
+
r.each {|rset|
|
52
|
+
results << {:Name => rset.name}
|
53
|
+
}
|
54
|
+
results = results.sort_by {|map|
|
55
|
+
map[:Name]
|
56
|
+
}
|
57
|
+
x4, y4 = status_render(x3+2, y3, "[Results]", results, :fields => [:Name])
|
58
|
+
|
59
|
+
(y3-y4-1).times do
|
60
|
+
print "\eD"
|
61
|
+
end
|
62
|
+
print "\eE"
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def status_render(movex, movey, msg, *args)
|
67
|
+
lines = cmd_render_table(*args).split("\n")
|
68
|
+
lines.pop # remove 'N rows in set' line
|
69
|
+
lines.unshift(msg)
|
70
|
+
#lines.unshift("")
|
71
|
+
|
72
|
+
print "\e[#{movey}A" if movey > 0
|
73
|
+
|
74
|
+
max_width = 0
|
75
|
+
height = 0
|
76
|
+
lines.each {|line|
|
77
|
+
print "\e[#{movex}C" if movex > 0
|
78
|
+
puts line
|
79
|
+
width = line.length
|
80
|
+
max_width = width if max_width < width
|
81
|
+
height += 1
|
82
|
+
}
|
83
|
+
|
84
|
+
return movex+max_width, height
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
data/lib/td/command/table.rb
CHANGED
@@ -103,15 +103,34 @@ module Command
|
|
103
103
|
|
104
104
|
def table_tail(op)
|
105
105
|
to = nil
|
106
|
-
count =
|
106
|
+
count = nil
|
107
107
|
|
108
108
|
op.on('-t', '--to TIME', 'end time of logs to get') {|s|
|
109
|
-
|
109
|
+
if s.to_i.to_s == s
|
110
|
+
to = s
|
111
|
+
else
|
112
|
+
to = Time.parse(s).to_i
|
113
|
+
end
|
110
114
|
}
|
111
115
|
op.on('-n', '--count N', 'number of logs to get', Integer) {|i|
|
112
116
|
count = i
|
113
117
|
}
|
114
118
|
|
119
|
+
if count == nil
|
120
|
+
# smart count calculation
|
121
|
+
begin
|
122
|
+
require "curses"
|
123
|
+
if Curses.stdscr.maxy - 1 <= 40
|
124
|
+
count = 5
|
125
|
+
else
|
126
|
+
count = 10
|
127
|
+
end
|
128
|
+
Curses.close_screen
|
129
|
+
rescue
|
130
|
+
count = 5
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
115
134
|
db_name, table_name = op.cmd_parse
|
116
135
|
|
117
136
|
client = get_client
|
data/lib/td/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: td
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-12-04 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: msgpack
|
16
|
-
requirement: &
|
16
|
+
requirement: &70131804842780 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.4.4
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70131804842780
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: json
|
27
|
-
requirement: &
|
27
|
+
requirement: &70131804842240 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.4.3
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70131804842240
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: hirb
|
38
|
-
requirement: &
|
38
|
+
requirement: &70131804841700 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,21 +43,21 @@ dependencies:
|
|
43
43
|
version: 0.4.5
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70131804841700
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: td-client
|
49
|
-
requirement: &
|
49
|
+
requirement: &70131804841140 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.8.
|
54
|
+
version: 0.8.5
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70131804841140
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: td-logger
|
60
|
-
requirement: &
|
60
|
+
requirement: &70131804840600 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: 0.3.7
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70131804840600
|
69
69
|
description:
|
70
70
|
email:
|
71
71
|
executables:
|
@@ -77,6 +77,7 @@ extra_rdoc_files:
|
|
77
77
|
files:
|
78
78
|
- lib/td.rb
|
79
79
|
- lib/td/command/account.rb
|
80
|
+
- lib/td/command/aggr.rb
|
80
81
|
- lib/td/command/apikey.rb
|
81
82
|
- lib/td/command/common.rb
|
82
83
|
- lib/td/command/db.rb
|
@@ -85,10 +86,12 @@ files:
|
|
85
86
|
- lib/td/command/job.rb
|
86
87
|
- lib/td/command/list.rb
|
87
88
|
- lib/td/command/query.rb
|
89
|
+
- lib/td/command/result.rb
|
88
90
|
- lib/td/command/runner.rb
|
89
91
|
- lib/td/command/sched.rb
|
90
92
|
- lib/td/command/schema.rb
|
91
93
|
- lib/td/command/server.rb
|
94
|
+
- lib/td/command/status.rb
|
92
95
|
- lib/td/command/table.rb
|
93
96
|
- lib/td/config.rb
|
94
97
|
- lib/td/version.rb
|