teradata-cli 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/COPYING +515 -0
- data/Gemfile +4 -0
- data/README.md +47 -0
- data/Rakefile +1 -0
- data/examples/query.rb +38 -0
- data/examples/show-queryband.rb +26 -0
- data/examples/tu/excel/excel.rb +86 -0
- data/examples/tu/excel/fill.rb +94 -0
- data/examples/tu/excel/template.xls +0 -0
- data/examples/tu/tusample1.rb +6 -0
- data/examples/tu/tusample2.rb +7 -0
- data/examples/tu/web/bitdao.rb +197 -0
- data/examples/tu/web/bitdao/teradata.rb +23 -0
- data/examples/tu/web/bitweb.rb +575 -0
- data/examples/tu/web/messages +0 -0
- data/examples/tu/web/server.rb +94 -0
- data/examples/tu/web/tdwalker.rb +42 -0
- data/examples/tu/web/template/database/show +56 -0
- data/examples/tu/web/template/footer +2 -0
- data/examples/tu/web/template/header +7 -0
- data/examples/update.rb +31 -0
- data/ext/teradata/cli/cli.c +363 -0
- data/ext/teradata/cli/extconf.rb +20 -0
- data/lib/teradata.rb +14 -0
- data/lib/teradata/cli.rb +4 -0
- data/lib/teradata/cli/version.rb +5 -0
- data/lib/teradata/connection.rb +1125 -0
- data/lib/teradata/dbobject.rb +453 -0
- data/lib/teradata/exception.rb +15 -0
- data/lib/teradata/utils.rb +184 -0
- data/teradata-cli.gemspec +24 -0
- data/test/all +7 -0
- data/test/rubyclitestutils.rb +99 -0
- data/test/test_connection.rb +298 -0
- data/test/test_dbobject.rb +153 -0
- data/test/test_record.rb +210 -0
- metadata +115 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# $Id: bitdao.rb 184 2009-08-12 08:46:22Z aamine $
|
2
|
+
|
3
|
+
require 'teradata'
|
4
|
+
|
5
|
+
module Teradata
|
6
|
+
class Error
|
7
|
+
include ::BitDAO::Error
|
8
|
+
end
|
9
|
+
|
10
|
+
class SQLError
|
11
|
+
include ::BitDAO::Error
|
12
|
+
end
|
13
|
+
|
14
|
+
class Connection # reopen
|
15
|
+
def error_class
|
16
|
+
Error
|
17
|
+
end
|
18
|
+
|
19
|
+
def sql_error_class
|
20
|
+
SQLError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,575 @@
|
|
1
|
+
# $Id: bitweb.rb 158 2009-05-22 10:13:19Z aamine $
|
2
|
+
|
3
|
+
require 'webrick/cgi'
|
4
|
+
require 'webrick/httpservlet/abstract'
|
5
|
+
require 'webrick/httpstatus'
|
6
|
+
begin
|
7
|
+
require 'fcgi'
|
8
|
+
rescue LoadError
|
9
|
+
end
|
10
|
+
require 'erb'
|
11
|
+
require 'yaml'
|
12
|
+
|
13
|
+
Socket.do_not_reverse_lookup = true
|
14
|
+
|
15
|
+
module BitWeb
|
16
|
+
|
17
|
+
class Error < StandardError; end
|
18
|
+
class RequestError < Error; end
|
19
|
+
|
20
|
+
class ValidationError < RequestError
|
21
|
+
def initialize(key, message)
|
22
|
+
super message
|
23
|
+
@key = key
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :key
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
class Interface
|
31
|
+
|
32
|
+
def initialize(webrick_conf = {})
|
33
|
+
@webrick_conf = webrick_conf
|
34
|
+
@handler = ($webinterface_context_cache ||= yield)
|
35
|
+
end
|
36
|
+
|
37
|
+
# for WEBrick servlet
|
38
|
+
def get_instance(server)
|
39
|
+
WEBrickServlet.new(server, @handler)
|
40
|
+
end
|
41
|
+
|
42
|
+
def main
|
43
|
+
if fastcgi?
|
44
|
+
FCGI.new(@webrick_conf).main(@handler)
|
45
|
+
else
|
46
|
+
# CGI, mod_ruby
|
47
|
+
CGI.new(@webrick_conf).main(@handler)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def fastcgi?
|
54
|
+
defined?(::FCGI) and ::FCGI.fastcgi?
|
55
|
+
end
|
56
|
+
|
57
|
+
def mod_ruby?
|
58
|
+
false # FIXME
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
class CGI < ::WEBrick::CGI
|
64
|
+
def main(handler)
|
65
|
+
@handler = handler
|
66
|
+
start
|
67
|
+
end
|
68
|
+
|
69
|
+
def do_GET(wreq, wres)
|
70
|
+
@handler.handle(wreq).update wres
|
71
|
+
end
|
72
|
+
|
73
|
+
alias do_POST do_GET
|
74
|
+
end
|
75
|
+
|
76
|
+
class FCGI < CGI
|
77
|
+
def main(handler)
|
78
|
+
@handler = handler
|
79
|
+
::FCGI.each_cgi_request do |req|
|
80
|
+
start req.env, req.in, req.out
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class WEBrickServlet < ::WEBrick::HTTPServlet::AbstractServlet
|
86
|
+
def do_GET(wreq, wres)
|
87
|
+
@options.first.handle(wreq).update wres
|
88
|
+
end
|
89
|
+
|
90
|
+
alias do_POST do_GET
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
module HTMLUtils
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
ESC = {
|
99
|
+
'&' => '&',
|
100
|
+
'"' => '"',
|
101
|
+
'<' => '<',
|
102
|
+
'>' => '>'
|
103
|
+
}
|
104
|
+
|
105
|
+
def escape_html(str)
|
106
|
+
table = ESC # optimize
|
107
|
+
str.gsub(/[&"<>]/) {|s| table[s] }
|
108
|
+
end
|
109
|
+
|
110
|
+
ESCrev = ESC.invert
|
111
|
+
|
112
|
+
def unescape_html(str)
|
113
|
+
table = ESCrev # optimize
|
114
|
+
str.gsub(/&\w+;/) {|s| table[s] }
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
module ClassUtils
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def find_class_from_current_context(name)
|
125
|
+
eval("#{current_class_path}::#{name}")
|
126
|
+
rescue NameError => err
|
127
|
+
raise RequestError, "unknown screen: #{id.inspect}"
|
128
|
+
end
|
129
|
+
|
130
|
+
def current_class_path
|
131
|
+
path = self.class.name.split('::')[0..-2]
|
132
|
+
path.empty? ? '' : "::#{path.join('::')}"
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
class RequestHandler
|
139
|
+
|
140
|
+
include ClassUtils
|
141
|
+
|
142
|
+
def initialize(log, views, models)
|
143
|
+
@log = log
|
144
|
+
@views = views
|
145
|
+
@models = models
|
146
|
+
@log.info(self.class) { "application started" }
|
147
|
+
end
|
148
|
+
|
149
|
+
def handle(webrick_req)
|
150
|
+
respond_to(Request.new(webrick_req))
|
151
|
+
rescue WEBrick::HTTPStatus::Status
|
152
|
+
raise
|
153
|
+
rescue => err
|
154
|
+
@log.error(self.class) { "#{err.class}: #{err.message}" }
|
155
|
+
return Response.new(ErrorScreen.new(err))
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def respond_to(req)
|
161
|
+
@log.info(self.class) {
|
162
|
+
"new request [#{req.controller.inspect}/#{req.command.inspect}]"
|
163
|
+
}
|
164
|
+
ctl = controller_class(req.controller).new(@log, @views, @models)
|
165
|
+
res = Response.new(ctl.handle(req))
|
166
|
+
@log.debug(self.class) { "controller returned" }
|
167
|
+
res
|
168
|
+
end
|
169
|
+
|
170
|
+
def controller_class(id)
|
171
|
+
find_class_from_current_context("#{id.capitalize}Controller")
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
class Controller
|
178
|
+
|
179
|
+
def self.depends(*names)
|
180
|
+
ivar_list = names.map {|n| "@#{n}" }.join(', ')
|
181
|
+
sym_list = names.map {|n| ":#{n}" }.join(', ')
|
182
|
+
module_eval(<<-End, __FILE__, __LINE__ + 1)
|
183
|
+
def initialize(log, views, models)
|
184
|
+
super log, views
|
185
|
+
#{ivar_list}, * = models.values_at(#{sym_list})
|
186
|
+
end
|
187
|
+
End
|
188
|
+
end
|
189
|
+
private_class_method :depends
|
190
|
+
|
191
|
+
def initialize(log, views)
|
192
|
+
@log = log
|
193
|
+
@views = views
|
194
|
+
end
|
195
|
+
|
196
|
+
def handle(req)
|
197
|
+
mid = "handle_#{req.command}"
|
198
|
+
unless respond_to?(mid, true)
|
199
|
+
@log.error(self.class) { "unknown command: #{req.command.inspect}" }
|
200
|
+
raise RequestError, "unknown command: #{req.command.inspect}"
|
201
|
+
end
|
202
|
+
@log.debug(self.class) { "dispatch: #{self.class}\##{mid}" }
|
203
|
+
__send__(mid, req)
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
class Request
|
210
|
+
|
211
|
+
def initialize(wreq)
|
212
|
+
@wreq = wreq
|
213
|
+
end
|
214
|
+
|
215
|
+
def peer_ipaddr
|
216
|
+
@peer_ipaddr ||= @wreq.peeraddr[2]
|
217
|
+
end
|
218
|
+
|
219
|
+
def peer_hostname
|
220
|
+
@peer_hostname ||= getnameinfo(peer_ipaddr).host
|
221
|
+
end
|
222
|
+
|
223
|
+
def getnameinfo(addr)
|
224
|
+
NameInfo.new(*Socket.getnameinfo([Socket::AF_UNSPEC, nil, addr]))
|
225
|
+
end
|
226
|
+
private :getnameinfo
|
227
|
+
|
228
|
+
NameInfo = Struct.new(:host, :port)
|
229
|
+
|
230
|
+
def controller
|
231
|
+
path_components[0] or
|
232
|
+
raise RequestError, "controller name did not given"
|
233
|
+
end
|
234
|
+
|
235
|
+
def command
|
236
|
+
path_components[1] or
|
237
|
+
raise RequestError, "command name did not given"
|
238
|
+
end
|
239
|
+
|
240
|
+
def path_components
|
241
|
+
@wreq.path_info.sub(%r<\A/>, '').split('/')
|
242
|
+
end
|
243
|
+
|
244
|
+
def [](name)
|
245
|
+
get(name)
|
246
|
+
end
|
247
|
+
|
248
|
+
def get(name)
|
249
|
+
val = @wreq.query[name]
|
250
|
+
s = (val && val.to_s)
|
251
|
+
if block_given?
|
252
|
+
yield(Parameter.new(name, s))
|
253
|
+
else
|
254
|
+
s
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def parameters(*keys)
|
259
|
+
h = {}
|
260
|
+
keys.each do |k|
|
261
|
+
h[k] = get(k)
|
262
|
+
end
|
263
|
+
h
|
264
|
+
end
|
265
|
+
|
266
|
+
class Parameter
|
267
|
+
def initialize(name, val)
|
268
|
+
@name = name
|
269
|
+
@value = val
|
270
|
+
end
|
271
|
+
|
272
|
+
def raw_value
|
273
|
+
@value
|
274
|
+
end
|
275
|
+
|
276
|
+
def string
|
277
|
+
@value.to_s
|
278
|
+
end
|
279
|
+
|
280
|
+
def time
|
281
|
+
Time.parse(@value.to_s)
|
282
|
+
rescue ArgumentError => err
|
283
|
+
validation_error "bad time format"
|
284
|
+
end
|
285
|
+
|
286
|
+
def date
|
287
|
+
Time.parse(@value.to_s)
|
288
|
+
rescue ArgumentError => err
|
289
|
+
validation_error "bad date format"
|
290
|
+
end
|
291
|
+
|
292
|
+
def must_date
|
293
|
+
unless %r<\A\d{4}[\-/]\d{1,2}[\-/]\d{1,2}\z> =~ @value
|
294
|
+
validation_error "bad date format"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def must_exist
|
299
|
+
unless @value
|
300
|
+
validation_error "not exist"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def must_string
|
305
|
+
must_exist
|
306
|
+
end
|
307
|
+
|
308
|
+
def must_not_empty
|
309
|
+
must_string
|
310
|
+
if @value.strip.empty?
|
311
|
+
validation_error "is empty"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def must_match(re)
|
316
|
+
unless re =~ @value
|
317
|
+
validation_error "bad format"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def must(msg = 'bad value')
|
322
|
+
unless yield(@value)
|
323
|
+
validation_error msg
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def validation_error(msg)
|
328
|
+
raise ValidationError.new(@name, msg)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
|
335
|
+
class Response
|
336
|
+
|
337
|
+
def initialize(screen)
|
338
|
+
@screen = screen
|
339
|
+
end
|
340
|
+
|
341
|
+
def update(wres)
|
342
|
+
wres.status = @screen.status if @screen.status
|
343
|
+
wres['Content-Type'] = @screen.content_type
|
344
|
+
body = @screen.body
|
345
|
+
wres['Content-Length'] = body.length
|
346
|
+
wres.body = body
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
|
352
|
+
class ViewManager
|
353
|
+
|
354
|
+
def initialize(log, template_dir, message_file, base_url, app_base_url = base_url)
|
355
|
+
@log = log
|
356
|
+
@template_dir = template_dir
|
357
|
+
@messages = Messages.load(message_file)
|
358
|
+
@base_url = base_url
|
359
|
+
@app_base_url = app_base_url
|
360
|
+
end
|
361
|
+
|
362
|
+
def new(screen_class, *args)
|
363
|
+
screen_class.new(@log, self, *args)
|
364
|
+
end
|
365
|
+
|
366
|
+
def run(id, binding)
|
367
|
+
erb = ERB.new(load(id))
|
368
|
+
erb.filename = id + '.erb'
|
369
|
+
erb.result(binding)
|
370
|
+
end
|
371
|
+
|
372
|
+
def load(id)
|
373
|
+
preproc(File.read("#{@template_dir}/#{id}"))
|
374
|
+
end
|
375
|
+
|
376
|
+
def preproc(template)
|
377
|
+
template.gsub(/^\.include ([\w\-]+)/) { load($1.untaint) }.untaint
|
378
|
+
end
|
379
|
+
private :preproc
|
380
|
+
|
381
|
+
def translate_message(key)
|
382
|
+
@messages[key]
|
383
|
+
end
|
384
|
+
|
385
|
+
def application_url(rel)
|
386
|
+
"#{@app_base_url}#{rel}"
|
387
|
+
end
|
388
|
+
|
389
|
+
def css_url(rel)
|
390
|
+
"#{@base_url}/css/#{rel}"
|
391
|
+
end
|
392
|
+
|
393
|
+
def js_url(rel)
|
394
|
+
"#{@base_url}/js/#{rel}"
|
395
|
+
end
|
396
|
+
|
397
|
+
def image_url(rel)
|
398
|
+
"#{@base_url}/images/#{rel}"
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
|
403
|
+
|
404
|
+
class Messages
|
405
|
+
def Messages.load(path)
|
406
|
+
new(YAML.load_file(path))
|
407
|
+
end
|
408
|
+
|
409
|
+
def initialize(h)
|
410
|
+
@messages = h
|
411
|
+
end
|
412
|
+
|
413
|
+
def [](key)
|
414
|
+
@messages[key] || key
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
|
419
|
+
class Screen
|
420
|
+
|
421
|
+
def status
|
422
|
+
nil
|
423
|
+
end
|
424
|
+
|
425
|
+
end
|
426
|
+
|
427
|
+
|
428
|
+
class ErrorScreen < Screen
|
429
|
+
|
430
|
+
include HTMLUtils
|
431
|
+
|
432
|
+
def initialize(error)
|
433
|
+
@error = error
|
434
|
+
end
|
435
|
+
|
436
|
+
def status
|
437
|
+
500
|
438
|
+
end
|
439
|
+
|
440
|
+
def content_type
|
441
|
+
# IE does not support XHTML, do not return "application/xhtml+xml".
|
442
|
+
'text/html'
|
443
|
+
end
|
444
|
+
|
445
|
+
def body
|
446
|
+
<<-EndHTML
|
447
|
+
<html>
|
448
|
+
<head><title>Error</title></head>
|
449
|
+
<body>
|
450
|
+
<h1>Error</h1>
|
451
|
+
<pre>#{escape_html(@error.message)} (#{escape_html(@error.class.name)})
|
452
|
+
#{@error.backtrace.map {|s| escape_html(s) }.join("\n")}</pre>
|
453
|
+
</body>
|
454
|
+
</html>
|
455
|
+
EndHTML
|
456
|
+
end
|
457
|
+
|
458
|
+
end
|
459
|
+
|
460
|
+
|
461
|
+
class TemplateScreen < Screen
|
462
|
+
|
463
|
+
def TemplateScreen.new_class(*attrs)
|
464
|
+
c = Class.new(self)
|
465
|
+
c.attributes(*attrs)
|
466
|
+
c
|
467
|
+
end
|
468
|
+
|
469
|
+
def TemplateScreen.attributes(*attrs)
|
470
|
+
ivar_list = attrs.map {|a| "@#{a}" }.join(', ')
|
471
|
+
param_list = attrs.join(', ')
|
472
|
+
module_eval(<<-End, __FILE__, __LINE__ + 1)
|
473
|
+
def initialize(log, views, #{attrs.join(', ')})
|
474
|
+
super log, views
|
475
|
+
#{ivar_list} = #{param_list}
|
476
|
+
end
|
477
|
+
End
|
478
|
+
module_eval {
|
479
|
+
attrs.each do |a|
|
480
|
+
attr_reader a
|
481
|
+
end
|
482
|
+
}
|
483
|
+
end
|
484
|
+
|
485
|
+
include HTMLUtils
|
486
|
+
|
487
|
+
def initialize(log, views)
|
488
|
+
@log = log
|
489
|
+
@views = views
|
490
|
+
end
|
491
|
+
|
492
|
+
def content_type
|
493
|
+
# IE does not support XHTML, do not return "application/xhtml+xml".
|
494
|
+
"text/html"
|
495
|
+
end
|
496
|
+
|
497
|
+
def body
|
498
|
+
@log.debug(self.class) { "running template: #{template_id}" }
|
499
|
+
@views.run(template_id, binding)
|
500
|
+
end
|
501
|
+
|
502
|
+
private
|
503
|
+
|
504
|
+
def template_id
|
505
|
+
c, s = self.class.name.split('::')[-2,2]
|
506
|
+
c.downcase.sub(/controller\z/, '') + '/' + s.downcase.sub(/screen\z/, '')
|
507
|
+
end
|
508
|
+
|
509
|
+
def _(key)
|
510
|
+
@views.translate_message(key)
|
511
|
+
end
|
512
|
+
|
513
|
+
def app_url(rel)
|
514
|
+
@views.application_url(rel)
|
515
|
+
end
|
516
|
+
|
517
|
+
def url(rel)
|
518
|
+
string(app_url(rel))
|
519
|
+
end
|
520
|
+
|
521
|
+
def css_url(rel)
|
522
|
+
@views.css_url(rel)
|
523
|
+
end
|
524
|
+
|
525
|
+
def css(rel)
|
526
|
+
string(css_url(rel))
|
527
|
+
end
|
528
|
+
|
529
|
+
def js_url(rel)
|
530
|
+
@views.js_url(rel)
|
531
|
+
end
|
532
|
+
|
533
|
+
def js(rel)
|
534
|
+
string(js_url(rel))
|
535
|
+
end
|
536
|
+
|
537
|
+
def image_url(rel)
|
538
|
+
@views.image_url(rel)
|
539
|
+
end
|
540
|
+
|
541
|
+
def img(rel)
|
542
|
+
string(image_url(rel))
|
543
|
+
end
|
544
|
+
|
545
|
+
def int(value)
|
546
|
+
value.to_i.to_s
|
547
|
+
end
|
548
|
+
|
549
|
+
def string(value)
|
550
|
+
escape_html(value.to_s)
|
551
|
+
end
|
552
|
+
|
553
|
+
alias h string
|
554
|
+
|
555
|
+
def multiline(value)
|
556
|
+
value.to_s.lines.map {|s| escape_html(s.strip) }.join('<br />')
|
557
|
+
end
|
558
|
+
|
559
|
+
def date(t)
|
560
|
+
t.strftime('%Y-%m-%d')
|
561
|
+
end
|
562
|
+
|
563
|
+
def timestamp(t)
|
564
|
+
t.strftime('%Y-%m-%d %H:%M:%S')
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
|
569
|
+
class Models < Struct
|
570
|
+
def values_at(*keys)
|
571
|
+
keys.map {|k| self[k] }
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
end
|