teradata-cli 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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