baza 0.0.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.
@@ -0,0 +1,155 @@
1
+ #This class handels various MySQL-column-specific operations.
2
+ class Baza::Driver::Mysql::Columns
3
+ #Constructor. Should not be called manually.
4
+ def initialize(args)
5
+ @args = args
6
+ end
7
+
8
+ #Returns the SQL for this column.
9
+ def data_sql(data)
10
+ raise "No type given." if !data["type"]
11
+
12
+ data["maxlength"] = 255 if data["type"] == "varchar" and !data.key?("maxlength")
13
+
14
+ sql = "`#{data["name"]}` #{data["type"]}"
15
+ sql << "(#{data["maxlength"]})" if data["maxlength"]
16
+ sql << " PRIMARY KEY" if data["primarykey"]
17
+ sql << " AUTO_INCREMENT" if data["autoincr"]
18
+ sql << " NOT NULL" if !data["null"]
19
+
20
+ if data.key?("default_func")
21
+ sql << " DEFAULT #{data["default_func"]}"
22
+ elsif data.key?("default") and data["default"] != false
23
+ sql << " DEFAULT '#{@args[:db].escape(data["default"])}'"
24
+ end
25
+
26
+ sql << " COMMENT '#{@args[:db].escape(data["comment"])}'" if data.key?("comment")
27
+ sql << " AFTER `#{@args[:db].esc_col(data["after"])}`" if data["after"] and !data["first"]
28
+ sql << " FIRST" if data["first"]
29
+ sql << " STORAGE #{data["storage"].to_s.upcase}" if data["storage"]
30
+
31
+ return sql
32
+ end
33
+ end
34
+
35
+ #This class handels every MySQL-column, that can be returned from a table-object.
36
+ class Baza::Driver::Mysql::Columns::Column
37
+ attr_reader :args
38
+
39
+ #Constructor. Should not be called manually.
40
+ def initialize(args)
41
+ @args = args
42
+ end
43
+
44
+ #Used to validate in Knj::Wrap_map.
45
+ def __object_unique_id__
46
+ return @args[:data][:Field]
47
+ end
48
+
49
+ #Returns the name of the column.
50
+ def name
51
+ return @args[:data][:Field]
52
+ end
53
+
54
+ #Returns the table-object that this column belongs to.
55
+ def table
56
+ return @args[:db].tables[@args[:table_name]]
57
+ end
58
+
59
+ #Returns all data of the column in the knjdb-format.
60
+ def data
61
+ return {
62
+ "type" => self.type,
63
+ "name" => self.name,
64
+ "null" => self.null?,
65
+ "maxlength" => self.maxlength,
66
+ "default" => self.default,
67
+ "primarykey" => self.primarykey?,
68
+ "autoincr" => self.autoincr?
69
+ }
70
+ end
71
+
72
+ #Returns the type of the column (integer, varchar etc.).
73
+ def type
74
+ if !@type
75
+ if match = @args[:data][:Type].match(/^([A-z]+)$/)
76
+ @maxlength = false
77
+ @type = match[0].to_sym
78
+ elsif match = @args[:data][:Type].match(/^decimal\((\d+),(\d+)\)$/)
79
+ @maxlength = "#{match[1]},#{match[2]}"
80
+ @type = :decimal
81
+ elsif match = @args[:data][:Type].match(/^enum\((.+)\)$/)
82
+ @maxlength = match[1]
83
+ @type = :enum
84
+ elsif match = @args[:data][:Type].match(/^(.+)\((\d+)\)/)
85
+ @maxlength = match[2].to_i
86
+ @type = match[1].to_sym
87
+ end
88
+
89
+ raise "Still not type from: '#{@args[:data][:Type]}'." if @type.to_s.strip.length <= 0
90
+ end
91
+
92
+ return @type
93
+ end
94
+
95
+ #Return true if the columns allows null. Otherwise false.
96
+ def null?
97
+ return false if @args[:data][:Null] == "NO"
98
+ return true
99
+ end
100
+
101
+ #Returns the maxlength.
102
+ def maxlength
103
+ self.type if !@maxlength
104
+ return @maxlength if @maxlength
105
+ return false
106
+ end
107
+
108
+ #Returns the default value for the column.
109
+ def default
110
+ return false if (self.type == "datetime" or self.type == "date") and @args[:data][:Default].to_s.strip.length <= 0
111
+ return false if (self.type == "int" or self.type == "bigint") and @args[:data][:Default].to_s.strip.length <= 0
112
+ return false if !@args[:data][:Default]
113
+ return @args[:data][:Default]
114
+ end
115
+
116
+ #Returns true if the column is the primary key. Otherwise false.
117
+ def primarykey?
118
+ return true if @args[:data][:Key] == "PRI"
119
+ return false
120
+ end
121
+
122
+ #Returns true if the column is auto-increasing. Otherwise false.
123
+ def autoincr?
124
+ return true if @args[:data][:Extra].index("auto_increment") != nil
125
+ return false
126
+ end
127
+
128
+ #Returns the comment for the column.
129
+ def comment
130
+ return @args[:data][:Comment]
131
+ end
132
+
133
+ #Drops the column from the table.
134
+ def drop
135
+ @args[:db].query("ALTER TABLE `#{@args[:table_name]}` DROP COLUMN `#{self.name}`")
136
+ return nil
137
+ end
138
+
139
+ #Changes the column properties by the given hash.
140
+ def change(data)
141
+ col_escaped = "`#{@args[:db].esc_col(self.name)}`"
142
+ table_escape = "`#{@args[:db].esc_table(self.table.name)}`"
143
+ newdata = data.clone
144
+
145
+ newdata["name"] = self.name if !newdata.key?("name")
146
+ newdata["type"] = self.type if !newdata.key?("type")
147
+ newdata["maxlength"] = self.maxlength if !newdata.key?("maxlength") and self.maxlength
148
+ newdata["null"] = self.null? if !newdata.key?("null")
149
+ newdata["default"] = self.default if !newdata.key?("default") and self.default
150
+ newdata.delete("primarykey") if newdata.key?("primarykey")
151
+
152
+ type_s = newdata["type"].to_s
153
+ @args[:db].query("ALTER TABLE #{table_escape} CHANGE #{col_escaped} #{@args[:db].cols.data_sql(newdata)}")
154
+ end
155
+ end
@@ -0,0 +1,69 @@
1
+ class Baza::Driver::Mysql::Indexes
2
+ def initialize(args)
3
+ @args = args
4
+ end
5
+ end
6
+
7
+ class Baza::Driver::Mysql::Indexes::Index
8
+ attr_reader :columns
9
+
10
+ def initialize(args)
11
+ @args = args
12
+ @columns = []
13
+ end
14
+
15
+ #Used to validate in Knj::Wrap_map.
16
+ def __object_unique_id__
17
+ return @args[:data][:Key_name]
18
+ end
19
+
20
+ def name
21
+ return @args[:data][:Key_name]
22
+ end
23
+
24
+ def table
25
+ return @args[:db].tables[@args[:table_name]]
26
+ end
27
+
28
+ def drop
29
+ sql = "DROP INDEX `#{self.name}` ON `#{self.table.name}`"
30
+
31
+ begin
32
+ @args[:db].query(sql)
33
+ rescue => e
34
+ #The index has already been dropped - ignore.
35
+ if e.message.index("check that column/key exists") != nil
36
+ #ignore.
37
+ else
38
+ raise e
39
+ end
40
+ end
41
+ end
42
+
43
+ def data
44
+ return {
45
+ "name" => name,
46
+ "columns" => @columns
47
+ }
48
+ end
49
+
50
+ #Returns true if the index is a unique-index.
51
+ def unique?
52
+ if @args[:data][:Index_type] == "UNIQUE"
53
+ return true
54
+ else
55
+ return false
56
+ end
57
+ end
58
+
59
+ #Returns true if the index is a primary-index.
60
+ def primary?
61
+ return true if @args[:data][:Index_type] == "BTREE"
62
+ return false
63
+ end
64
+
65
+ #Inspect crashes if this is not present? - knj.
66
+ def to_s
67
+ return "#<#{self.class.name}>"
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ class Baza::Driver::Mysql::Sqlspecs < Baza::Sqlspecs
2
+ def strftime(val, colstr)
3
+ return "DATE_FORMAT(#{colstr}, '#{val}')"
4
+ end
5
+ end
@@ -0,0 +1,443 @@
1
+ require "monitor"
2
+
3
+ #This class handels various MySQL-table-specific behaviour.
4
+ class Baza::Driver::Mysql::Tables
5
+ attr_reader :db, :list
6
+
7
+ #Constructor. This should not be called manually.
8
+ def initialize(args)
9
+ @args = args
10
+ @db = @args[:db]
11
+ @subtype = @db.opts[:subtype]
12
+ @list_mutex = Monitor.new
13
+ @list = Wref_map.new
14
+ @list_should_be_reloaded = true
15
+ end
16
+
17
+ #Cleans the wref-map.
18
+ def clean
19
+ @list.clean
20
+ end
21
+
22
+ #Returns a table by the given table-name.
23
+ def [](table_name)
24
+ table_name = table_name.to_s
25
+
26
+ begin
27
+ return @list[table_name]
28
+ rescue Wref::Recycled
29
+ #ignore.
30
+ end
31
+
32
+ self.list(:name => table_name) do |table_obj|
33
+ return table_obj if table_obj.name == table_name
34
+ end
35
+
36
+ raise Errno::ENOENT, "Table was not found: #{table_name}."
37
+ end
38
+
39
+ #Yields the tables of the current database.
40
+ def list(args = {})
41
+ ret = {} unless block_given?
42
+
43
+ sql = "SHOW TABLE STATUS"
44
+ sql << " WHERE `Name` = '#{@db.esc(args[:name])}'" if args[:name]
45
+
46
+ @list_mutex.synchronize do
47
+ @db.q(sql) do |d_tables|
48
+ obj = @list.get!(d_tables[:Name])
49
+
50
+ if !obj
51
+ obj = Baza::Driver::Mysql::Tables::Table.new(
52
+ :db => @db,
53
+ :data => d_tables
54
+ )
55
+ @list[d_tables[:Name]] = obj
56
+ end
57
+
58
+ if block_given?
59
+ yield(obj)
60
+ else
61
+ ret[d_tables[:Name]] = obj
62
+ end
63
+ end
64
+ end
65
+
66
+ if block_given?
67
+ return nil
68
+ else
69
+ return ret
70
+ end
71
+ end
72
+
73
+ #Creates a new table by the given name and data.
74
+ def create(name, data, args = nil)
75
+ raise "No columns was given for '#{name}'." if !data["columns"] or data["columns"].empty?
76
+
77
+ sql = "CREATE"
78
+ sql << " TEMPORARY" if data["temp"]
79
+ sql << " TABLE `#{name}` ("
80
+
81
+ first = true
82
+ data["columns"].each do |col_data|
83
+ sql << ", " if !first
84
+ first = false if first
85
+ col_data.delete("after") if col_data["after"]
86
+ sql << @db.cols.data_sql(col_data)
87
+ end
88
+
89
+ if data["indexes"] and !data["indexes"].empty?
90
+ sql << ", "
91
+ sql << Baza::Driver::Mysql::Tables::Table.create_indexes(data["indexes"], {
92
+ :db => @db,
93
+ :return_sql => true,
94
+ :create => false,
95
+ :on_table => false,
96
+ :table_name => name
97
+ })
98
+ end
99
+
100
+ sql << ")"
101
+
102
+ return [sql] if args and args[:return_sql]
103
+ @db.query(sql)
104
+ end
105
+ end
106
+
107
+ class Baza::Driver::Mysql::Tables::Table
108
+ attr_reader :list
109
+
110
+ def initialize(args)
111
+ @args = args
112
+ @db = args[:db]
113
+ @data = args[:data]
114
+ @subtype = @db.opts[:subtype]
115
+ @list = Wref_map.new
116
+ @indexes_list = Wref_map.new
117
+
118
+ raise "Could not figure out name from: '#{@data}'." if @data[:Name].to_s.strip.length <= 0
119
+ end
120
+
121
+ def reload
122
+ @data = @db.q("SHOW TABLE STATUS WHERE `Name` = '#{@db.esc(self.name)}'").fetch
123
+ end
124
+
125
+ #Used to validate in Knj::Wrap_map.
126
+ def __object_unique_id__
127
+ return @data[:Name]
128
+ end
129
+
130
+ def name
131
+ return @data[:Name]
132
+ end
133
+
134
+ def drop
135
+ raise "Cant drop native table: '#{self.name}'." if self.native?
136
+ @db.query("DROP TABLE `#{self.name}`")
137
+ end
138
+
139
+ #Returns true if the table is safe to drop.
140
+ def native?
141
+ return true if @db.q("SELECT DATABASE() AS db").fetch[:db] == "mysql"
142
+ return false
143
+ end
144
+
145
+ def optimize
146
+ @db.query("OPTIMIZE TABLE `#{self.name}`")
147
+ return self
148
+ end
149
+
150
+ def rows_count
151
+ return @data[:Rows].to_i
152
+ end
153
+
154
+ def column(name)
155
+ name = name.to_s
156
+
157
+ if col = @list.get!(name)
158
+ return @list[name]
159
+ end
160
+
161
+ self.columns(:name => name) do |col|
162
+ return col if col.name == name
163
+ end
164
+
165
+ raise Errno::ENOENT, "Column not found: '#{name}'."
166
+ end
167
+
168
+ def columns(args = nil)
169
+ @db.cols
170
+ ret = {}
171
+ sql = "SHOW FULL COLUMNS FROM `#{self.name}`"
172
+ sql << " WHERE `Field` = '#{@db.esc(args[:name])}'" if args and args.key?(:name)
173
+
174
+ @db.q(sql) do |d_cols|
175
+ obj = @list.get!(d_cols[:Field].to_s)
176
+
177
+ if !obj
178
+ obj = Baza::Driver::Mysql::Columns::Column.new(
179
+ :table_name => self.name,
180
+ :db => @db,
181
+ :data => d_cols
182
+ )
183
+ @list[d_cols[:Field].to_s] = obj
184
+ end
185
+
186
+ if block_given?
187
+ yield(obj)
188
+ else
189
+ ret[d_cols[:Field].to_s] = obj
190
+ end
191
+ end
192
+
193
+ if block_given?
194
+ return nil
195
+ else
196
+ return ret
197
+ end
198
+ end
199
+
200
+ def indexes(args = nil)
201
+ @db.indexes
202
+ ret = {}
203
+
204
+ sql = "SHOW INDEX FROM `#{self.name}`"
205
+ sql << " WHERE `Key_name` = '#{@db.esc(args[:name])}'" if args and args.key?(:name)
206
+
207
+ @db.q(sql) do |d_indexes|
208
+ next if d_indexes[:Key_name] == "PRIMARY"
209
+
210
+ obj = @indexes_list.get!(d_indexes[:Key_name].to_s)
211
+
212
+ if !obj
213
+ obj = Baza::Driver::Mysql::Indexes::Index.new(
214
+ :table_name => self.name,
215
+ :db => @db,
216
+ :data => d_indexes
217
+ )
218
+ obj.columns << d_indexes[:Column_name]
219
+ @indexes_list[d_indexes[:Key_name].to_s] = obj
220
+ end
221
+
222
+ if block_given?
223
+ yield(obj)
224
+ else
225
+ ret[d_indexes[:Key_name].to_s] = obj
226
+ end
227
+ end
228
+
229
+ if block_given?
230
+ return nil
231
+ else
232
+ return ret
233
+ end
234
+ end
235
+
236
+ def index(name)
237
+ name = name.to_s
238
+
239
+ if index = @indexes_list.get!(name)
240
+ return index
241
+ end
242
+
243
+ self.indexes(:name => name) do |index|
244
+ return index if index.name.to_s == name
245
+ end
246
+
247
+ raise Errno::ENOENT, "Index not found: #{name}."
248
+ end
249
+
250
+ def create_columns(col_arr)
251
+ col_arr.each do |col_data|
252
+ sql = "ALTER TABLE `#{self.name}` ADD COLUMN #{@db.cols.data_sql(col_data)};"
253
+ @db.query(sql)
254
+ end
255
+ end
256
+
257
+ def create_indexes(index_arr, args = {})
258
+ return Baza::Driver::Mysql::Tables::Table.create_indexes(index_arr, args.merge(:table_name => self.name, :db => @db))
259
+ end
260
+
261
+ def self.create_indexes(index_arr, args = {})
262
+ db = args[:db]
263
+
264
+ if args[:return_sql]
265
+ sql = ""
266
+ first = true
267
+ end
268
+
269
+ index_arr.each do |index_data|
270
+ if !args[:return_sql]
271
+ sql = ""
272
+ end
273
+
274
+ if args[:create] or !args.key?(:create)
275
+ sql << "CREATE"
276
+ end
277
+
278
+ if index_data.is_a?(String)
279
+ index_data = {"name" => index_data, "columns" => [index_data]}
280
+ end
281
+
282
+ raise "No name was given." if !index_data.key?("name") or index_data["name"].strip.length <= 0
283
+ raise "No columns was given on index: '#{index_data["name"]}'." if !index_data["columns"] or index_data["columns"].empty?
284
+
285
+ if args[:return_sql]
286
+ if first
287
+ first = false
288
+ else
289
+ sql << ", "
290
+ end
291
+ end
292
+
293
+ sql << " UNIQUE" if index_data["unique"]
294
+ sql << " INDEX `#{db.esc_col(index_data["name"])}`"
295
+
296
+ if args[:on_table] or !args.key?(:on_table)
297
+ sql << " ON `#{db.esc_table(args[:table_name])}`"
298
+ end
299
+
300
+ sql << " ("
301
+
302
+ first = true
303
+ index_data["columns"].each do |col_name|
304
+ sql << ", " if !first
305
+ first = false if first
306
+
307
+ sql << "`#{db.esc_col(col_name)}`"
308
+ end
309
+
310
+ sql << ")"
311
+
312
+ if !args[:return_sql]
313
+ db.query(sql)
314
+ end
315
+ end
316
+
317
+ if args[:return_sql]
318
+ return sql
319
+ else
320
+ return nil
321
+ end
322
+ end
323
+
324
+ def rename(newname)
325
+ oldname = self.name
326
+ @db.query("ALTER TABLE `#{oldname}` RENAME TO `#{newname}`")
327
+ @db.tables.list[newname] = self
328
+ @db.tables.list.delete(oldname)
329
+ @data[:Name] = newname
330
+ end
331
+
332
+ def truncate
333
+ @db.query("TRUNCATE `#{self.name}`")
334
+ return self
335
+ end
336
+
337
+ def data
338
+ ret = {
339
+ "name" => self.name,
340
+ "columns" => [],
341
+ "indexes" => []
342
+ }
343
+
344
+ columns.each do |name, column|
345
+ ret["columns"] << column.data
346
+ end
347
+
348
+ indexes.each do |name, index|
349
+ ret["indexes"] << index.data if name != "PRIMARY"
350
+ end
351
+
352
+ return ret
353
+ end
354
+
355
+ def insert(data)
356
+ @db.insert(self.name, data)
357
+ end
358
+
359
+ #Returns the current engine of the table.
360
+ def engine
361
+ return @data[:Engine]
362
+ end
363
+
364
+ def clone(newname, args = {})
365
+ raise "Invalid name." if newname.to_s.strip.empty?
366
+
367
+ sql = "CREATE TABLE `#{@db.esc_table(newname)}` ("
368
+ first = true
369
+ pkey_found = false
370
+ pkeys = []
371
+
372
+ self.columns do |col|
373
+ sql << ", " if !first
374
+ first = false if first
375
+
376
+ col_data = col.data
377
+ pkey_found = true if !pkey_found and col_data["primarykey"] and args[:force_single_pkey]
378
+
379
+ if args[:no_pkey] or (pkey_found and col_data["primarykey"] and args[:force_single_pkey])
380
+ col_data["primarykey"] = false
381
+ end
382
+
383
+ if col_data["primarykey"]
384
+ pkeys << col_data["name"]
385
+ col_data.delete("primarykey")
386
+ end
387
+
388
+ if args[:all_cols_storage]
389
+ col_data["storage"] = args[:all_cols_storage]
390
+ end
391
+
392
+ sql << @db.cols.data_sql(col_data)
393
+ end
394
+
395
+ if !pkeys.empty?
396
+ sql << ", PRIMARY KEY ("
397
+
398
+ first = true
399
+ pkeys.each do |pkey|
400
+ sql << ", " if !first
401
+ first = false if first
402
+ sql << "`#{@db.esc_col(pkey)}`"
403
+ end
404
+
405
+ sql << ")"
406
+ end
407
+
408
+ sql << ")"
409
+ sql << " TABLESPACE #{args[:tablespace]}" if args[:tablespace]
410
+ sql << " ENGINE=#{args[:engine]}" if args[:engine]
411
+ sql << ";"
412
+
413
+ puts sql
414
+
415
+ #Create table.
416
+ @db.query(sql)
417
+
418
+
419
+ #Insert data of previous data in a single query.
420
+ @db.query("INSERT INTO `#{newname}` SELECT * FROM `#{self.name}`")
421
+
422
+
423
+ #Create indexes.
424
+ new_table = @db.tables[newname]
425
+ indexes_list = []
426
+ self.indexes do |index|
427
+ indexes_list << index.data if !index.primary?
428
+ end
429
+
430
+ new_table.create_indexes(indexes_list)
431
+
432
+
433
+ #Return new table.
434
+ return new_table
435
+ end
436
+
437
+ #Changes the engine for a table.
438
+ def engine=(newengine)
439
+ raise "Invalid engine: '#{newengine}'." if !newengine.to_s.match(/^[A-z]+$/)
440
+ @db.query("ALTER TABLE `#{@db.esc_table(self.name)}` ENGINE = #{newengine}") if self.engine.to_s != newengine.to_s
441
+ @data[:Engine] = newengine
442
+ end
443
+ end