td 0.7.5 → 0.8.0
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/ChangeLog +5 -0
- data/lib/td/command/common.rb +1 -1
- data/lib/td/version.rb +1 -1
- metadata +21 -7
- data/lib/td/api.rb +0 -455
- data/lib/td/client.rb +0 -380
data/ChangeLog
CHANGED
data/lib/td/command/common.rb
CHANGED
data/lib/td/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: td
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 63
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 8
|
9
|
+
- 0
|
10
|
+
version: 0.8.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sadayuki Furuhashi
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-08-
|
18
|
+
date: 2011-08-21 00:00:00 +09:00
|
19
19
|
default_executable: td
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -66,6 +66,22 @@ dependencies:
|
|
66
66
|
version: 0.4.5
|
67
67
|
type: :runtime
|
68
68
|
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: td-client
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 63
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
- 8
|
81
|
+
- 0
|
82
|
+
version: 0.8.0
|
83
|
+
type: :runtime
|
84
|
+
version_requirements: *id004
|
69
85
|
description:
|
70
86
|
email:
|
71
87
|
executables:
|
@@ -76,8 +92,6 @@ extra_rdoc_files:
|
|
76
92
|
- ChangeLog
|
77
93
|
- README.rdoc
|
78
94
|
files:
|
79
|
-
- lib/td/api.rb
|
80
|
-
- lib/td/client.rb
|
81
95
|
- lib/td/command/account.rb
|
82
96
|
- lib/td/command/common.rb
|
83
97
|
- lib/td/command/database.rb
|
data/lib/td/api.rb
DELETED
@@ -1,455 +0,0 @@
|
|
1
|
-
|
2
|
-
module TreasureData
|
3
|
-
|
4
|
-
|
5
|
-
class APIError < StandardError
|
6
|
-
end
|
7
|
-
|
8
|
-
class AuthError < APIError
|
9
|
-
end
|
10
|
-
|
11
|
-
class AlreadyExistsError < APIError
|
12
|
-
end
|
13
|
-
|
14
|
-
class NotFoundError < APIError
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
class API
|
19
|
-
def initialize(apikey)
|
20
|
-
require 'json'
|
21
|
-
@apikey = apikey
|
22
|
-
end
|
23
|
-
|
24
|
-
# TODO error check & raise appropriate errors
|
25
|
-
|
26
|
-
attr_reader :apikey
|
27
|
-
|
28
|
-
def self.validate_database_name(name)
|
29
|
-
name = name.to_s
|
30
|
-
if name.empty?
|
31
|
-
raise "Empty name is not allowed"
|
32
|
-
end
|
33
|
-
if name.length < 3 || 32 < name.length
|
34
|
-
raise "Name must be 3 to 32 characters, got #{name.length} characters."
|
35
|
-
end
|
36
|
-
unless name =~ /^([a-z0-9_]+)$/
|
37
|
-
raise "Name must consist only of alphabets, numbers, '_'."
|
38
|
-
end
|
39
|
-
name
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.validate_table_name(name)
|
43
|
-
validate_database_name(name)
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.validate_column_name(name)
|
47
|
-
name = name.to_s
|
48
|
-
if name.empty?
|
49
|
-
raise "Empty column name is not allowed"
|
50
|
-
end
|
51
|
-
if 32 < name.length
|
52
|
-
raise "Column name must be to 32 characters, got #{name.length} characters."
|
53
|
-
end
|
54
|
-
unless name =~ /^([a-z0-9_]+)$/
|
55
|
-
raise "Column name must consist only of alphabets, numbers, '_'."
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.normalize_type_name(name)
|
60
|
-
case name
|
61
|
-
when /int/i, /integer/i
|
62
|
-
"int"
|
63
|
-
when /long/i, /bigint/i
|
64
|
-
"long"
|
65
|
-
when /string/i
|
66
|
-
"string"
|
67
|
-
when /float/i
|
68
|
-
"float"
|
69
|
-
when /double/i
|
70
|
-
"double"
|
71
|
-
else
|
72
|
-
raise "Type name must eather of int, long, string float or double"
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
####
|
77
|
-
## Database API
|
78
|
-
##
|
79
|
-
|
80
|
-
# => [name:String]
|
81
|
-
def list_databases
|
82
|
-
code, body, res = get("/v3/database/list")
|
83
|
-
if code != "200"
|
84
|
-
raise_error("List databases failed", res)
|
85
|
-
end
|
86
|
-
# TODO format check
|
87
|
-
js = JSON.load(body)
|
88
|
-
names = js["databases"].map {|dbinfo| dbinfo['name'] }
|
89
|
-
return names
|
90
|
-
end
|
91
|
-
|
92
|
-
# => true
|
93
|
-
def delete_database(db)
|
94
|
-
code, body, res = post("/v3/database/delete/#{e db}")
|
95
|
-
if code != "200"
|
96
|
-
raise_error("Delete database failed", res)
|
97
|
-
end
|
98
|
-
return true
|
99
|
-
end
|
100
|
-
|
101
|
-
# => true
|
102
|
-
def create_database(db)
|
103
|
-
code, body, res = post("/v3/database/create/#{e db}")
|
104
|
-
if code != "200"
|
105
|
-
raise_error("Create database failed", res)
|
106
|
-
end
|
107
|
-
return true
|
108
|
-
end
|
109
|
-
|
110
|
-
|
111
|
-
####
|
112
|
-
## Table API
|
113
|
-
##
|
114
|
-
|
115
|
-
# => {name:String => [type:Symbol, count:Integer]}
|
116
|
-
def list_tables(db)
|
117
|
-
code, body, res = get("/v3/table/list/#{e db}")
|
118
|
-
if code != "200"
|
119
|
-
raise_error("List tables failed", res)
|
120
|
-
end
|
121
|
-
# TODO format check
|
122
|
-
js = JSON.load(body)
|
123
|
-
result = {}
|
124
|
-
js["tables"].map {|m|
|
125
|
-
name = m['name']
|
126
|
-
type = (m['type'] || '?').to_sym
|
127
|
-
count = (m['count'] || 0).to_i # TODO?
|
128
|
-
schema = JSON.parse(m['schema'] || '[]')
|
129
|
-
result[name] = [type, schema, count]
|
130
|
-
}
|
131
|
-
return result
|
132
|
-
end
|
133
|
-
|
134
|
-
def create_log_or_item_table(db, table, type)
|
135
|
-
code, body, res = post("/v3/table/create/#{e db}/#{e table}/#{type}")
|
136
|
-
if code != "200"
|
137
|
-
raise_error("Create #{type} table failed", res)
|
138
|
-
end
|
139
|
-
return true
|
140
|
-
end
|
141
|
-
private :create_log_or_item_table
|
142
|
-
|
143
|
-
# => true
|
144
|
-
def create_log_table(db, table)
|
145
|
-
create_table(db, table, :log)
|
146
|
-
end
|
147
|
-
|
148
|
-
# => true
|
149
|
-
def create_item_table(db, table)
|
150
|
-
create_table(db, table, :item)
|
151
|
-
end
|
152
|
-
|
153
|
-
def create_table(db, table, type)
|
154
|
-
schema = schema.to_s
|
155
|
-
code, body, res = post("/v3/table/create/#{e db}/#{e table}/#{type}")
|
156
|
-
if code != "200"
|
157
|
-
raise_error("Create #{type} table failed", res)
|
158
|
-
end
|
159
|
-
return true
|
160
|
-
end
|
161
|
-
private :create_table
|
162
|
-
|
163
|
-
# => true
|
164
|
-
def update_schema(db, table, schema_json)
|
165
|
-
code, body, res = post("/v3/table/update-schema/#{e db}/#{e table}", {'schema'=>schema_json})
|
166
|
-
if code != "200"
|
167
|
-
raise_error("Create schema table failed", res)
|
168
|
-
end
|
169
|
-
return true
|
170
|
-
end
|
171
|
-
|
172
|
-
# => type:Symbol
|
173
|
-
def delete_table(db, table)
|
174
|
-
code, body, res = post("/v3/table/delete/#{e db}/#{e table}")
|
175
|
-
if code != "200"
|
176
|
-
raise_error("Drop table failed", res)
|
177
|
-
end
|
178
|
-
# TODO format check
|
179
|
-
js = JSON.load(body)
|
180
|
-
type = (js['type'] || '?').to_sym
|
181
|
-
return type
|
182
|
-
end
|
183
|
-
|
184
|
-
|
185
|
-
####
|
186
|
-
## Job API
|
187
|
-
##
|
188
|
-
|
189
|
-
# => [(jobId:String, type:Symbol, status:String, start_at:String, end_at:String)]
|
190
|
-
def list_jobs(from=0, to=nil)
|
191
|
-
params = {}
|
192
|
-
params['from'] = from.to_s if from
|
193
|
-
params['to'] = to.to_s if to
|
194
|
-
code, body, res = get("/v3/job/list", params)
|
195
|
-
if code != "200"
|
196
|
-
raise_error("List jobs failed", res)
|
197
|
-
end
|
198
|
-
# TODO format check
|
199
|
-
js = JSON.load(body)
|
200
|
-
result = []
|
201
|
-
js['jobs'].each {|m|
|
202
|
-
job_id = m['job_id']
|
203
|
-
type = (m['type'] || '?').to_sym
|
204
|
-
status = m['status']
|
205
|
-
query = m['query']
|
206
|
-
start_at = m['start_at']
|
207
|
-
end_at = m['end_at']
|
208
|
-
result << [job_id, type, status, query, start_at, end_at]
|
209
|
-
}
|
210
|
-
return result
|
211
|
-
end
|
212
|
-
|
213
|
-
# => (type:Symbol, status:String, result:String, url:String)
|
214
|
-
def show_job(job_id)
|
215
|
-
code, body, res = get("/v3/job/show/#{e job_id}")
|
216
|
-
if code != "200"
|
217
|
-
raise_error("Show job failed", res)
|
218
|
-
end
|
219
|
-
# TODO format check
|
220
|
-
js = JSON.load(body)
|
221
|
-
# TODO debug
|
222
|
-
type = (js['type'] || '?').to_sym # TODO
|
223
|
-
query = js['query']
|
224
|
-
status = js['status']
|
225
|
-
debug = js['debug']
|
226
|
-
url = js['url']
|
227
|
-
start_at = js['start_at']
|
228
|
-
end_at = js['end_at']
|
229
|
-
return [type, query, status, url, debug, start_at, end_at]
|
230
|
-
end
|
231
|
-
|
232
|
-
def job_result(job_id)
|
233
|
-
require 'msgpack'
|
234
|
-
code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'})
|
235
|
-
if code != "200"
|
236
|
-
raise_error("Get job result failed", res)
|
237
|
-
end
|
238
|
-
result = []
|
239
|
-
MessagePack::Unpacker.new.feed_each(body) {|row|
|
240
|
-
result << row
|
241
|
-
}
|
242
|
-
return result
|
243
|
-
end
|
244
|
-
|
245
|
-
def job_result_format(job_id, format)
|
246
|
-
# TODO chunked encoding
|
247
|
-
code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>format})
|
248
|
-
if code != "200"
|
249
|
-
raise_error("Get job result failed", res)
|
250
|
-
end
|
251
|
-
return body
|
252
|
-
end
|
253
|
-
|
254
|
-
def job_result_each(job_id, &block)
|
255
|
-
# TODO chunked encoding
|
256
|
-
require 'msgpack'
|
257
|
-
code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>'msgpack'})
|
258
|
-
if code != "200"
|
259
|
-
raise_error("Get job result failed", res)
|
260
|
-
end
|
261
|
-
result = []
|
262
|
-
MessagePack::Unpacker.new.feed_each(body) {|row|
|
263
|
-
yield row
|
264
|
-
}
|
265
|
-
nil
|
266
|
-
end
|
267
|
-
|
268
|
-
def job_result_raw(job_id, format)
|
269
|
-
code, body, res = get("/v3/job/result/#{e job_id}", {'format'=>format})
|
270
|
-
if code != "200"
|
271
|
-
raise_error("Get job result failed", res)
|
272
|
-
end
|
273
|
-
return body
|
274
|
-
end
|
275
|
-
|
276
|
-
# => jobId:String
|
277
|
-
def hive_query(q, db=nil)
|
278
|
-
code, body, res = post("/v3/job/issue/hive/#{e db}", {'query'=>q})
|
279
|
-
if code != "200"
|
280
|
-
raise_error("Query failed", res)
|
281
|
-
end
|
282
|
-
# TODO format check
|
283
|
-
js = JSON.load(body)
|
284
|
-
return js['job_id'].to_s
|
285
|
-
end
|
286
|
-
|
287
|
-
|
288
|
-
####
|
289
|
-
## Import API
|
290
|
-
##
|
291
|
-
|
292
|
-
# => time:Float
|
293
|
-
def import(db, table, format, stream, stream_size=stream.lstat.size)
|
294
|
-
code, body, res = put("/v3/table/import/#{e db}/#{e table}/#{format}", stream, stream_size)
|
295
|
-
if code[0] != ?2
|
296
|
-
raise_error("Import failed", res)
|
297
|
-
end
|
298
|
-
# TODO format check
|
299
|
-
js = JSON.load(body)
|
300
|
-
time = js['time'].to_f
|
301
|
-
return time
|
302
|
-
end
|
303
|
-
|
304
|
-
|
305
|
-
####
|
306
|
-
## User API
|
307
|
-
##
|
308
|
-
|
309
|
-
# apikey:String
|
310
|
-
def authenticate(user, password)
|
311
|
-
code, body, res = post("/v3/user/authenticate", {'user'=>user, 'password'=>password})
|
312
|
-
if code != "200"
|
313
|
-
raise_error("Authentication failed", res)
|
314
|
-
end
|
315
|
-
# TODO format check
|
316
|
-
js = JSON.load(body)
|
317
|
-
apikey = js['apikey']
|
318
|
-
return apikey
|
319
|
-
end
|
320
|
-
|
321
|
-
####
|
322
|
-
## Server Status API
|
323
|
-
##
|
324
|
-
|
325
|
-
# => status:String
|
326
|
-
def server_status
|
327
|
-
code, body, res = get('/v3/system/server_status')
|
328
|
-
if code != "200"
|
329
|
-
return "Server is down (#{code})"
|
330
|
-
end
|
331
|
-
# TODO format check
|
332
|
-
js = JSON.load(body)
|
333
|
-
status = js['status']
|
334
|
-
return status
|
335
|
-
end
|
336
|
-
|
337
|
-
private
|
338
|
-
host = 'api.treasure-data.com'
|
339
|
-
port = 80
|
340
|
-
if e = ENV['TD_API_SERVER']
|
341
|
-
host, port_ = e.split(':',2)
|
342
|
-
port_ = port_.to_i
|
343
|
-
port = port_ if port_ != 0
|
344
|
-
end
|
345
|
-
|
346
|
-
HOST = host
|
347
|
-
PORT = port
|
348
|
-
USE_SSL = false
|
349
|
-
BASE_URL = ''
|
350
|
-
|
351
|
-
def get(url, params=nil)
|
352
|
-
http, header = new_http
|
353
|
-
|
354
|
-
path = BASE_URL + url
|
355
|
-
if params && !params.empty?
|
356
|
-
path << "?"+params.map {|k,v|
|
357
|
-
"#{k}=#{e v}"
|
358
|
-
}.join('&')
|
359
|
-
end
|
360
|
-
|
361
|
-
request = Net::HTTP::Get.new(path, header)
|
362
|
-
|
363
|
-
response = http.request(request)
|
364
|
-
return [response.code, response.body, response]
|
365
|
-
end
|
366
|
-
|
367
|
-
def post(url, params=nil)
|
368
|
-
http, header = new_http
|
369
|
-
|
370
|
-
path = BASE_URL + url
|
371
|
-
|
372
|
-
request = Net::HTTP::Post.new(path, header)
|
373
|
-
request.set_form_data(params) if params
|
374
|
-
|
375
|
-
response = http.request(request)
|
376
|
-
return [response.code, response.body, response]
|
377
|
-
end
|
378
|
-
|
379
|
-
def put(url, stream, stream_size)
|
380
|
-
http, header = new_http
|
381
|
-
|
382
|
-
path = BASE_URL + url
|
383
|
-
|
384
|
-
header['Content-Type'] = 'application/octet-stream'
|
385
|
-
header['Content-Length'] = stream_size.to_s
|
386
|
-
|
387
|
-
request = Net::HTTP::Put.new(url, header)
|
388
|
-
if request.respond_to?(:body_stream=)
|
389
|
-
request.body_stream = stream
|
390
|
-
else # Ruby 1.8
|
391
|
-
request.body = stream.read
|
392
|
-
end
|
393
|
-
|
394
|
-
response = http.request(request)
|
395
|
-
return [response.code, response.body, response]
|
396
|
-
end
|
397
|
-
|
398
|
-
def new_http
|
399
|
-
require 'net/http'
|
400
|
-
require 'time'
|
401
|
-
|
402
|
-
http = Net::HTTP.new(HOST, PORT)
|
403
|
-
if USE_SSL
|
404
|
-
http.use_ssl = true
|
405
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
406
|
-
store = OpenSSL::X509::Store.new
|
407
|
-
http.cert_store = store
|
408
|
-
end
|
409
|
-
|
410
|
-
#http.read_timeout = options[:read_timeout]
|
411
|
-
|
412
|
-
header = {}
|
413
|
-
if @apikey
|
414
|
-
header['Authorization'] = "TD1 #{apikey}"
|
415
|
-
end
|
416
|
-
header['Date'] = Time.now.rfc2822
|
417
|
-
|
418
|
-
return http, header
|
419
|
-
end
|
420
|
-
|
421
|
-
def raise_error(msg, res)
|
422
|
-
begin
|
423
|
-
js = JSON.load(res.body)
|
424
|
-
msg = js['message']
|
425
|
-
error_code = js['error_code']
|
426
|
-
|
427
|
-
if res.code == "404"
|
428
|
-
raise NotFoundError, "#{error_code}: #{msg}"
|
429
|
-
elsif res.code == "409"
|
430
|
-
raise AlreadyExistsError, "#{error_code}: #{msg}"
|
431
|
-
else
|
432
|
-
raise APIError, "#{error_code}: #{msg}"
|
433
|
-
end
|
434
|
-
|
435
|
-
rescue
|
436
|
-
if res.code == "404"
|
437
|
-
raise NotFoundError, "#{msg}: #{res.body}"
|
438
|
-
elsif res.code == "409"
|
439
|
-
raise AlreadyExistsError, "#{msg}: #{res.body}"
|
440
|
-
else
|
441
|
-
raise APIError, "#{msg}: #{res.body}"
|
442
|
-
end
|
443
|
-
end
|
444
|
-
# TODO error
|
445
|
-
end
|
446
|
-
|
447
|
-
def e(s)
|
448
|
-
require 'cgi'
|
449
|
-
CGI.escape(s.to_s)
|
450
|
-
end
|
451
|
-
end
|
452
|
-
|
453
|
-
|
454
|
-
end
|
455
|
-
|
data/lib/td/client.rb
DELETED
@@ -1,380 +0,0 @@
|
|
1
|
-
require 'time'
|
2
|
-
require 'td/api'
|
3
|
-
|
4
|
-
module TreasureData
|
5
|
-
|
6
|
-
class Client
|
7
|
-
def self.authenticate(user, password)
|
8
|
-
api = API.new(nil)
|
9
|
-
apikey = api.authenticate(user, password)
|
10
|
-
new(apikey)
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.server_status
|
14
|
-
api = API.new(nil)
|
15
|
-
api.server_status
|
16
|
-
end
|
17
|
-
|
18
|
-
def initialize(apikey)
|
19
|
-
@api = API.new(apikey)
|
20
|
-
end
|
21
|
-
|
22
|
-
attr_reader :api
|
23
|
-
|
24
|
-
def apikey
|
25
|
-
@api.apikey
|
26
|
-
end
|
27
|
-
|
28
|
-
def server_status
|
29
|
-
@api.server_status
|
30
|
-
end
|
31
|
-
|
32
|
-
# => true
|
33
|
-
def create_database(db_name)
|
34
|
-
@api.create_database(db_name)
|
35
|
-
end
|
36
|
-
|
37
|
-
# => true
|
38
|
-
def delete_database(db_name)
|
39
|
-
@api.delete_database(db_name)
|
40
|
-
end
|
41
|
-
|
42
|
-
# => [Database]
|
43
|
-
def databases
|
44
|
-
names = @api.list_databases
|
45
|
-
names.map {|db_name|
|
46
|
-
Database.new(self, db_name)
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
# => Database
|
51
|
-
def database(db_name)
|
52
|
-
names = @api.list_databases
|
53
|
-
names.each {|n|
|
54
|
-
if n == db_name
|
55
|
-
return Database.new(self, db_name)
|
56
|
-
end
|
57
|
-
}
|
58
|
-
raise NotFoundError, "Database '#{db_name}' does not exist"
|
59
|
-
end
|
60
|
-
|
61
|
-
# => true
|
62
|
-
def create_log_table(db_name, table_name)
|
63
|
-
@api.create_log_table(db_name, table_name)
|
64
|
-
end
|
65
|
-
|
66
|
-
# => true
|
67
|
-
def create_item_table(db_name, table_name)
|
68
|
-
@api.create_item_table(db_name, table_name)
|
69
|
-
end
|
70
|
-
|
71
|
-
# => true
|
72
|
-
def update_schema(db_name, table_name, schema)
|
73
|
-
@api.update_schema(db_name, table_name, schema.to_json)
|
74
|
-
end
|
75
|
-
|
76
|
-
# => type:Symbol
|
77
|
-
def delete_table(db_name, table_name)
|
78
|
-
@api.delete_table(db_name, table_name)
|
79
|
-
end
|
80
|
-
|
81
|
-
# => [Table]
|
82
|
-
def tables(db_name)
|
83
|
-
m = @api.list_tables(db_name)
|
84
|
-
m.map {|table_name,(type,schema,count)|
|
85
|
-
schema = Schema.new.from_json(schema)
|
86
|
-
Table.new(self, db_name, table_name, type, schema, count)
|
87
|
-
}
|
88
|
-
end
|
89
|
-
|
90
|
-
# => Table
|
91
|
-
def table(db_name, table_name)
|
92
|
-
tables(db_name).each {|t|
|
93
|
-
if t.name == table_name
|
94
|
-
return t
|
95
|
-
end
|
96
|
-
}
|
97
|
-
raise NotFoundError, "Table '#{db_name}.#{table_name}' does not exist"
|
98
|
-
end
|
99
|
-
|
100
|
-
# => Job
|
101
|
-
def query(db_name, q)
|
102
|
-
job_id = @api.hive_query(q, db_name)
|
103
|
-
Job.new(self, job_id, :hive, q) # TODO url
|
104
|
-
end
|
105
|
-
|
106
|
-
# => [Job=]
|
107
|
-
def jobs(from=nil, to=nil)
|
108
|
-
js = @api.list_jobs(from, to)
|
109
|
-
js.map {|job_id,type,status,query,start_at,end_at|
|
110
|
-
Job.new(self, job_id, type, query, status, nil, nil, start_at, end_at)
|
111
|
-
}
|
112
|
-
end
|
113
|
-
|
114
|
-
# => Job
|
115
|
-
def job(job_id)
|
116
|
-
job_id = job_id.to_s
|
117
|
-
type, query, status, url, debug, start_at, end_at = @api.show_job(job_id)
|
118
|
-
Job.new(self, job_id, type, query, status, url, debug, start_at, end_at)
|
119
|
-
end
|
120
|
-
|
121
|
-
# => type:Symbol, url:String
|
122
|
-
def job_status(job_id)
|
123
|
-
type, query, status, url, debug, start_at, end_at = @api.show_job(job_id)
|
124
|
-
return query, status, url, debug, start_at, end_at
|
125
|
-
end
|
126
|
-
|
127
|
-
# => result:[{column:String=>value:Object]
|
128
|
-
def job_result(job_id)
|
129
|
-
@api.job_result(job_id)
|
130
|
-
end
|
131
|
-
|
132
|
-
# => result:String
|
133
|
-
def job_result_format(job_id, format)
|
134
|
-
@api.job_result_format(job_id, format)
|
135
|
-
end
|
136
|
-
|
137
|
-
# => nil
|
138
|
-
def job_result_each(job_id, &block)
|
139
|
-
@api.job_result_each(job_id, &block)
|
140
|
-
end
|
141
|
-
|
142
|
-
# => time:Flaot
|
143
|
-
def import(db_name, table_name, format, stream, stream_size=stream.lstat.size)
|
144
|
-
@api.import(db_name, table_name, format, stream, stream_size)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
|
149
|
-
class Model
|
150
|
-
def initialize(client)
|
151
|
-
@client = client
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
class Database < Model
|
156
|
-
def initialize(client, db_name, tables=nil)
|
157
|
-
super(client)
|
158
|
-
@db_name = db_name
|
159
|
-
@tables = tables
|
160
|
-
end
|
161
|
-
|
162
|
-
def name
|
163
|
-
@db_name
|
164
|
-
end
|
165
|
-
|
166
|
-
def tables
|
167
|
-
update_tables! unless @tables
|
168
|
-
@tables
|
169
|
-
end
|
170
|
-
|
171
|
-
def create_log_table(name)
|
172
|
-
@client.create_log_table(@db_name, name)
|
173
|
-
end
|
174
|
-
|
175
|
-
def create_item_table(name)
|
176
|
-
@client.create_item_table(@db_name, name)
|
177
|
-
end
|
178
|
-
|
179
|
-
def table(table_name)
|
180
|
-
@client.table(@db_name, table_name)
|
181
|
-
end
|
182
|
-
|
183
|
-
def delete
|
184
|
-
@client.delete_database(@db_name)
|
185
|
-
end
|
186
|
-
|
187
|
-
def update_tables!
|
188
|
-
@tables = @client.tables(@db_name)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
class Table < Model
|
193
|
-
def initialize(client, db_name, table_name, type, schema, count)
|
194
|
-
super(client)
|
195
|
-
@db_name = db_name
|
196
|
-
@table_name = table_name
|
197
|
-
@type = type
|
198
|
-
@schema = schema
|
199
|
-
@count = count
|
200
|
-
end
|
201
|
-
|
202
|
-
attr_reader :type, :db_name, :table_name, :schema, :count
|
203
|
-
|
204
|
-
alias database_name db_name
|
205
|
-
alias name table_name
|
206
|
-
|
207
|
-
def database
|
208
|
-
@client.database(@db_name)
|
209
|
-
end
|
210
|
-
|
211
|
-
def identifier
|
212
|
-
"#{@db_name}.#{@table_name}"
|
213
|
-
end
|
214
|
-
|
215
|
-
def delete
|
216
|
-
@client.delete_table(@db_name, @table_name)
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
class Schema
|
221
|
-
class Field
|
222
|
-
def initialize(name, type)
|
223
|
-
@name = name
|
224
|
-
@type = type
|
225
|
-
end
|
226
|
-
attr_reader :name
|
227
|
-
attr_reader :type
|
228
|
-
end
|
229
|
-
|
230
|
-
def self.parse(cols)
|
231
|
-
fields = cols.split(',').map {|col|
|
232
|
-
name, type, *_ = col.split(':')
|
233
|
-
Field.new(name, type)
|
234
|
-
}
|
235
|
-
Schema.new(fields)
|
236
|
-
end
|
237
|
-
|
238
|
-
def initialize(fields=[])
|
239
|
-
@fields = fields
|
240
|
-
end
|
241
|
-
|
242
|
-
attr_reader :fields
|
243
|
-
|
244
|
-
def add_field(name, type)
|
245
|
-
@fields << Field.new(name, type)
|
246
|
-
end
|
247
|
-
|
248
|
-
def merge(schema)
|
249
|
-
nf = @fields.dup
|
250
|
-
schema.fields.each {|f|
|
251
|
-
if i = nf.find_index {|sf| sf.name == f.name }
|
252
|
-
nf[i] = f
|
253
|
-
else
|
254
|
-
nf << f
|
255
|
-
end
|
256
|
-
}
|
257
|
-
Schema.new(nf)
|
258
|
-
end
|
259
|
-
|
260
|
-
def to_json(*args)
|
261
|
-
@fields.map {|f| [f.name, f.type] }.to_json(*args)
|
262
|
-
end
|
263
|
-
|
264
|
-
def from_json(obj)
|
265
|
-
@fields = obj.map {|f|
|
266
|
-
Field.new(f[0], f[1])
|
267
|
-
}
|
268
|
-
self
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
class Job < Model
|
273
|
-
def initialize(client, job_id, type, query, status=nil, url=nil, debug=nil, start_at=nil, end_at=nil, result=nil)
|
274
|
-
super(client)
|
275
|
-
@job_id = job_id
|
276
|
-
@type = type
|
277
|
-
@url = url
|
278
|
-
@query = query
|
279
|
-
@status = status
|
280
|
-
@debug = debug
|
281
|
-
@start_at = start_at
|
282
|
-
@end_at = end_at
|
283
|
-
@result = result
|
284
|
-
end
|
285
|
-
|
286
|
-
attr_reader :job_id, :type
|
287
|
-
|
288
|
-
def wait(timeout=nil)
|
289
|
-
# TODO
|
290
|
-
end
|
291
|
-
|
292
|
-
def query
|
293
|
-
update_status! unless @query
|
294
|
-
@query
|
295
|
-
end
|
296
|
-
|
297
|
-
def status
|
298
|
-
update_status! unless @status
|
299
|
-
@status
|
300
|
-
end
|
301
|
-
|
302
|
-
def url
|
303
|
-
update_status! unless @url
|
304
|
-
@url
|
305
|
-
end
|
306
|
-
|
307
|
-
def debug
|
308
|
-
update_status! unless @debug
|
309
|
-
@debug
|
310
|
-
end
|
311
|
-
|
312
|
-
def start_at
|
313
|
-
update_status! unless @start_at
|
314
|
-
@start_at && !@start_at.empty? ? Time.parse(@start_at) : nil
|
315
|
-
end
|
316
|
-
|
317
|
-
def end_at
|
318
|
-
update_status! unless @end_at
|
319
|
-
@end_at && !@end_at.empty? ? Time.parse(@end_at) : nil
|
320
|
-
end
|
321
|
-
|
322
|
-
def result
|
323
|
-
unless @result
|
324
|
-
return nil unless finished?
|
325
|
-
@result = @client.job_result(@job_id)
|
326
|
-
end
|
327
|
-
@result
|
328
|
-
end
|
329
|
-
|
330
|
-
def result_format(format)
|
331
|
-
return nil unless finished?
|
332
|
-
@client.job_result_format(@job_id, format)
|
333
|
-
end
|
334
|
-
|
335
|
-
def result_each(&block)
|
336
|
-
if @result
|
337
|
-
@result.each(&block)
|
338
|
-
else
|
339
|
-
@client.job_result_each(@job_id, &block)
|
340
|
-
end
|
341
|
-
nil
|
342
|
-
end
|
343
|
-
|
344
|
-
def finished?
|
345
|
-
update_status! unless @status
|
346
|
-
if @status == "success" || @status == "error"
|
347
|
-
return true
|
348
|
-
else
|
349
|
-
return false
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
def running?
|
354
|
-
!finished?
|
355
|
-
end
|
356
|
-
|
357
|
-
def success?
|
358
|
-
update_status! unless @status
|
359
|
-
@status == "success"
|
360
|
-
end
|
361
|
-
|
362
|
-
def error?
|
363
|
-
update_status! unless @status
|
364
|
-
@status == "error"
|
365
|
-
end
|
366
|
-
|
367
|
-
def update_status!
|
368
|
-
query, status, url, debug, start_at, end_at = @client.job_status(@job_id)
|
369
|
-
@query = query
|
370
|
-
@status = status
|
371
|
-
@url = url
|
372
|
-
@debug = debug
|
373
|
-
@start_at = start_at
|
374
|
-
@end_at = end_at
|
375
|
-
self
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
end
|
380
|
-
|