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.
@@ -0,0 +1,453 @@
1
+ #
2
+ # $Id: dbobject.rb 7 2010-03-04 16:54:09Z tdaoki $
3
+ #
4
+ # Copyright (C) 2009,2010 Teradata Japan, LTD.
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU LGPL2, Lesser General Public License version 2.
9
+ #
10
+
11
+ require 'teradata/utils'
12
+ require 'teradata/connection'
13
+ require 'teradata/exception'
14
+
15
+ module Teradata
16
+
17
+ class ObjectError < Error; end
18
+
19
+ class Connection # reopen
20
+
21
+ include SQLUtils
22
+
23
+ ROOT_DATABASE_NAME = 'DBC'
24
+
25
+ def root_database
26
+ User.new(ROOT_DATABASE_NAME, self)
27
+ end
28
+
29
+ alias dbc root_database
30
+
31
+ def database(name)
32
+ kind = database_kind(name) or
33
+ raise ObjectError, "no such database: #{name.inspect}"
34
+ kind.new(name, self)
35
+ end
36
+
37
+ def database_kind(name)
38
+ recs = entries(<<-EndSQL)
39
+ SELECT dbKind
40
+ FROM dbc.databases
41
+ WHERE databaseName = #{sql_string name}
42
+ EndSQL
43
+ return nil if recs.empty?
44
+ if recs.size > 1
45
+ raise "multiple database entries exist in dbc.databases???: #{name.inspect}"
46
+ end
47
+ class_from_kind_char(recs.first[0].strip.upcase)
48
+ end
49
+
50
+ def database_exist?(name)
51
+ database_kind(name) ? true : false
52
+ end
53
+
54
+ alias database? database_exist?
55
+
56
+ def user_exist?(name)
57
+ database_kind(name) == User
58
+ end
59
+
60
+ alias user? user_exist?
61
+
62
+ def parent_databases(name)
63
+ parents = [Database.new(name, self)]
64
+ while true
65
+ db = database_owner(parents.last.name)
66
+ break unless db
67
+ parents.push db
68
+ end
69
+ parents.shift # remove myself
70
+ parents
71
+ end
72
+
73
+ # Database owner.
74
+ # Returns nil for root database (DBC).
75
+ def database_owner(name)
76
+ return nil if name.downcase == 'dbc'
77
+ owners = entries(<<-EndSQL).map {|rec| [rec[0].strip.upcase, rec[1].strip] }
78
+ SELECT owner.dbKind, self.ownerName
79
+ FROM dbc.databases self INNER JOIN dbc.databases owner
80
+ ON self.ownerName = owner.databaseName
81
+ WHERE self.databaseName = #{sql_string name}
82
+ EndSQL
83
+ if owners.empty?
84
+ raise ObjectError, "database not exist: #{name.inspect}"
85
+ end
86
+ if owners.size > 1
87
+ raise "multiple database entries exist in dbc.databases???: #{name.inspect}"
88
+ end
89
+ kind_char, owner = owners.first
90
+ return nil if owner.downcase == name.downcase
91
+ new_database(kind_char, owner)
92
+ end
93
+
94
+ def child_databases(name)
95
+ entries(<<-EndSQL).map {|rec| new_database(rec[0].strip.upcase, rec[1].strip) }
96
+ SELECT dbKind, databaseName
97
+ FROM dbc.databases
98
+ WHERE ownerName = #{sql_string name}
99
+ EndSQL
100
+ end
101
+
102
+ def class_from_kind_char(c)
103
+ c == 'U' ? User : Database
104
+ end
105
+ private :class_from_kind_char
106
+
107
+ def new_database(kind_char, name)
108
+ class_from_kind_char(kind_char).new(name, self)
109
+ end
110
+ private :new_database
111
+
112
+ Perms = Struct.new(:current, :max, :peak)
113
+
114
+ def database_own_perms(name)
115
+ perms = entries(<<-EndSQL).first
116
+ SELECT
117
+ sum(currentPerm)
118
+ , sum(maxPerm)
119
+ , sum(peakPerm)
120
+ FROM dbc.diskSpace
121
+ WHERE databaseName = #{sql_string name}
122
+ EndSQL
123
+ unless perms
124
+ raise ObjectError, "database does not exist in dbc.diskSpace: #{name.inspect}"
125
+ end
126
+ Perms.new(* perms.to_a.map {|n| n.to_i })
127
+ end
128
+
129
+ def database_total_perms(name)
130
+ recs = entries(<<-EndSQL)
131
+ SELECT
132
+ sum(ds.currentPerm)
133
+ , sum(ds.maxPerm)
134
+ , sum(ds.peakPerm)
135
+ FROM
136
+ (
137
+ (SELECT d.databaseName, d.databaseName FROM dbc.databases d)
138
+ UNION
139
+ (SELECT parent, child FROM dbc.children)
140
+ ) as c (parent, child)
141
+ INNER JOIN dbc.diskSpace ds
142
+ ON c.child = ds.databaseName
143
+ WHERE
144
+ c.parent = #{sql_string name}
145
+ EndSQL
146
+ if recs.empty?
147
+ raise ObjectError, "database does not exist in dbc.diskSpace: #{@name.inspect}"
148
+ end
149
+ if recs.size > 1
150
+ raise "multiple database entry exist on dbc.diskSpace???: #{name.inspect}; size=#{recs.size}"
151
+ end
152
+ Perms.new(* recs.first.to_a.map {|n| n.to_i })
153
+ end
154
+
155
+ def tables(database)
156
+ recs = entries(<<-EndSQL)
157
+ SELECT trim(tableName)
158
+ , sum(currentPerm)
159
+ , sum(peakPerm)
160
+ FROM dbc.tableSize
161
+ WHERE databaseName = #{sql_string database}
162
+ GROUP BY tableName
163
+ EndSQL
164
+ c = ::Teradata::Table
165
+ recs.map {|rec|
166
+ name, curr, peak = *rec.to_a
167
+ c.new(database, name, curr.to_i, peak.to_i)
168
+ }
169
+ end
170
+
171
+ def views(database)
172
+ fetch_objects(database, ::Teradata::View)
173
+ end
174
+
175
+ def fetch_objects(database, obj_class)
176
+ # FIXME??: use dbc.tvm
177
+ entries("HELP DATABASE #{database}")\
178
+ .select {|rec| rec[1].strip.upcase == obj_class.type_char }\
179
+ .map {|rec| obj_class.new(database, rec[0].strip) }
180
+ end
181
+ private :fetch_objects
182
+
183
+ def objects(database)
184
+ entries("HELP DATABASE #{database}").map {|rec|
185
+ ::Teradata::DBObject.create(rec[1].strip, database, rec[0].strip)
186
+ }
187
+ end
188
+
189
+ def column(obj, name)
190
+ recs = entries(<<-EndSQL)
191
+ SELECT * FROM dbc.columns
192
+ WHERE databaseName = #{sql_string obj.database}
193
+ AND tableName = #{sql_string obj.unqualified_name}
194
+ AND columnName = #{sql_string name}
195
+ EndSQL
196
+ unless recs.size == 1
197
+ raise ArgumentError, "could not specify a column: #{obj.name}.#{name}"
198
+ end
199
+ Column.for_record(recs.first)
200
+ end
201
+
202
+ end
203
+
204
+
205
+ class Database
206
+
207
+ def initialize(name, conn)
208
+ @name = name
209
+ @connection = conn
210
+ invalidate_cache
211
+ end
212
+
213
+ attr_reader :name
214
+
215
+ def invalidate_cache
216
+ @parents = nil
217
+ @children = nil
218
+ @tables = nil
219
+ @own_perms = nil
220
+ @total_perms = nil
221
+ end
222
+
223
+ def inspect
224
+ "\#<#{self.class} #{@name}>"
225
+ end
226
+
227
+ def user?
228
+ false
229
+ end
230
+
231
+ def owner
232
+ parents.first
233
+ end
234
+
235
+ alias parent owner
236
+
237
+ def parents
238
+ @parents ||= @connection.parent_databases(@name)
239
+ end
240
+
241
+ def children
242
+ @children ||= @connection.child_databases(@name)
243
+ end
244
+
245
+ def tables
246
+ @tables ||= @connection.tables(@name)
247
+ end
248
+
249
+ def own_current_perm
250
+ load_own_perms
251
+ @own_perms.current
252
+ end
253
+
254
+ alias current_perm own_current_perm
255
+
256
+ def own_max_perm
257
+ load_own_perms
258
+ @own_perms.max
259
+ end
260
+
261
+ alias max_perm own_max_perm
262
+
263
+ def own_peak_perm
264
+ load_own_perms
265
+ @own_perms.peak
266
+ end
267
+
268
+ alias peak_perm own_peak_perm
269
+
270
+ def load_own_perms
271
+ @own_perms ||= @connection.database_own_perms(@name)
272
+ end
273
+ private :load_own_perms
274
+
275
+ def total_current_perm
276
+ load_total_perms
277
+ @total_perms.current
278
+ end
279
+
280
+ def total_max_perm
281
+ load_total_perms
282
+ @total_perms.max
283
+ end
284
+
285
+ def total_peak_perm
286
+ load_total_perms
287
+ @total_perms.peak
288
+ end
289
+
290
+ def load_total_perms
291
+ @total_perms ||= @connection.database_total_perms(@name)
292
+ end
293
+ private :load_total_perms
294
+
295
+ end
296
+
297
+
298
+ class User < Database
299
+
300
+ def user?
301
+ true
302
+ end
303
+
304
+ end
305
+
306
+
307
+ class DBObject
308
+
309
+ def DBObject.intern(spec)
310
+ spec.kind_of?(DBObject) ? spec : parse(spec)
311
+ end
312
+
313
+ def DBObject.parse(spec)
314
+ new(* spec.split('.', 2))
315
+ end
316
+
317
+ OBJECT_TYPES = {}
318
+
319
+ class << DBObject
320
+ def declare_type(c, name)
321
+ OBJECT_TYPES[c] = self
322
+ @type_char = c
323
+ @type_name = name
324
+ end
325
+
326
+ attr_reader :type_char
327
+ attr_reader :type_name
328
+ end
329
+
330
+ def DBObject.create(type_char, *args)
331
+ cls = OBJECT_TYPES[type_char] or
332
+ raise ArgumentError, "unknown type char: #{type_char.inspect}"
333
+ cls.new(*args)
334
+ end
335
+
336
+ def initialize(x, y = nil)
337
+ if y
338
+ @database = x
339
+ @name = y
340
+ else
341
+ @database = nil
342
+ @name = x
343
+ end
344
+ end
345
+
346
+ attr_reader :database
347
+
348
+ def unqualified_name
349
+ @name
350
+ end
351
+
352
+ def name
353
+ @database ? "#{@database}.#{@name}" : @name
354
+ end
355
+
356
+ alias to_s name
357
+
358
+ def inspect
359
+ "\#<#{self.class} #{name}>"
360
+ end
361
+
362
+ def ==(other)
363
+ other.kind_of?(self.class) and self.name == other.name
364
+ end
365
+
366
+ def type_char
367
+ self.class.type_char
368
+ end
369
+
370
+ def type_name
371
+ self.class.type_name
372
+ end
373
+
374
+ end
375
+
376
+
377
+ class Table < DBObject
378
+ declare_type 'T', 'TABLE'
379
+
380
+ def initialize(x, y = nil, curr = nil, peak = nil)
381
+ super x, y
382
+ @current_perm = curr
383
+ @peak_perm = peak
384
+ end
385
+
386
+ attr_reader :current_perm
387
+ alias size current_perm
388
+ attr_reader :peak_perm
389
+ end
390
+
391
+ class View < DBObject
392
+ declare_type 'V', 'VIEW'
393
+ end
394
+
395
+ class Macro < DBObject
396
+ declare_type 'M', 'MACRO'
397
+ end
398
+
399
+ class Procedure < DBObject
400
+ declare_type 'P', 'PROCEDURE'
401
+ end
402
+
403
+ class JoinIndex < DBObject
404
+ declare_type 'J', 'JOIN INDEX'
405
+ end
406
+
407
+ class HashIndex < DBObject
408
+ declare_type 'N', 'HASH INDEX'
409
+ end
410
+
411
+ COLUMN_ATTRIBUTES = [
412
+ :database_name,
413
+ :table_name,
414
+ :column_name,
415
+ :column_format,
416
+ :column_title,
417
+ :sp_parameter_type,
418
+ :column_type,
419
+ :column_udt_name,
420
+ :column_length,
421
+ :default_value,
422
+ :nullable,
423
+ :comment_string,
424
+ :decimal_total_digits,
425
+ :decimal_fractional_digits,
426
+ :column_id,
427
+ :upper_case_flag,
428
+ :comprressible,
429
+ :compress_value,
430
+ :column_constraint,
431
+ :constraint_count,
432
+ :creator_name,
433
+ :create_timestamp,
434
+ :last_alter_name,
435
+ :last_alter_timestamp,
436
+ :char_type,
437
+ :id_col_type,
438
+ :access_count,
439
+ :last_access_timestamp,
440
+ :compress_value_list
441
+ ]
442
+
443
+ Column = Struct.new(* COLUMN_ATTRIBUTES)
444
+
445
+ class Column # reopen
446
+ extend MetadataUtils
447
+
448
+ def Column.for_record(rec)
449
+ new(* adjust_list_size(rec.to_a, COLUMN_ATTRIBUTES.size))
450
+ end
451
+ end
452
+
453
+ end