dbdiff 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/COPYING +340 -0
- data/lib/dbdiff.rb +63 -33
- data/lib/dbdiff/database.rb +50 -4
- data/lib/dbdiff/dbdiff_version.rb +3 -0
- data/lib/dbdiff/delta.rb +89 -0
- data/lib/dbdiff/function.rb +14 -0
- data/lib/dbdiff/procedure.rb +13 -0
- data/lib/dbdiff/routine.rb +30 -0
- data/lib/dbdiff/row.rb +9 -1
- data/lib/dbdiff/table.rb +2 -2
- data/lib/dbdiff/trigger.rb +36 -0
- data/lib/dbdiff/view.rb +33 -0
- data/test/function/source.sql +3 -0
- data/test/function/target.sql +1 -0
- data/test/modify_function/source.sql +3 -0
- data/test/modify_function/target.sql +2 -0
- data/test/modify_procedure/source.sql +8 -0
- data/test/modify_procedure/target.sql +9 -0
- data/test/modify_trigger/source.sql +9 -0
- data/test/modify_trigger/target.sql +9 -0
- data/test/modify_view/source.sql +7 -0
- data/test/modify_view/target.sql +7 -0
- data/test/procedure/source.sql +8 -0
- data/test/procedure/target.sql +1 -0
- data/test/suite.rb +3 -0
- data/test/test_dbdiff.rb +13 -5
- data/test/test_function.rb +69 -0
- data/test/test_procedure.rb +64 -0
- data/test/test_row.rb +45 -9
- data/test/test_trigger.rb +68 -0
- data/test/trigger/source.sql +9 -0
- data/test/trigger/target.sql +7 -0
- data/test/view/source.sql +7 -0
- data/test/view/target.sql +9 -0
- metadata +37 -2
data/lib/dbdiff/database.rb
CHANGED
@@ -2,6 +2,10 @@ require 'rubygems'
|
|
2
2
|
require 'mysql'
|
3
3
|
require 'dbdiff/delta'
|
4
4
|
require 'dbdiff/table_element'
|
5
|
+
require 'dbdiff/view'
|
6
|
+
require 'dbdiff/trigger'
|
7
|
+
require 'dbdiff/procedure'
|
8
|
+
require 'dbdiff/function'
|
5
9
|
require 'dbdiff/table'
|
6
10
|
require 'dbdiff/column'
|
7
11
|
require 'dbdiff/foreign_key'
|
@@ -11,7 +15,7 @@ class DbDiff
|
|
11
15
|
|
12
16
|
# XXX validate state of information_schema?
|
13
17
|
class Database
|
14
|
-
attr_reader :dbh, :tables, :name, :host, :user, :password
|
18
|
+
attr_reader :dbh, :tables, :procedures, :functions, :views, :name, :host, :user, :password
|
15
19
|
attr_accessor :deltas
|
16
20
|
|
17
21
|
# XXX add version check
|
@@ -24,6 +28,10 @@ class DbDiff
|
|
24
28
|
@deltas = []
|
25
29
|
|
26
30
|
initialize_tables
|
31
|
+
initialize_functions
|
32
|
+
initialize_procedures
|
33
|
+
initialize_triggers
|
34
|
+
initialize_views
|
27
35
|
initialize_columns
|
28
36
|
initialize_keys
|
29
37
|
initialize_foreign_keys
|
@@ -34,9 +42,38 @@ class DbDiff
|
|
34
42
|
@tables + @deltas.find_all{|d| d.class == Delta::AddTable }.map {|x| x.element}
|
35
43
|
end
|
36
44
|
|
45
|
+
def initialize_triggers
|
46
|
+
select_is(:triggers, :trigger_schema => self.name) do |h|
|
47
|
+
t = Trigger.new(h)
|
48
|
+
tb = table(t.table_name)
|
49
|
+
tb.triggers << t if tb
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize_functions
|
54
|
+
@functions = []
|
55
|
+
select_mysql(:proc, :type => 'FUNCTION') do |h|
|
56
|
+
@functions << Function.new(h)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize_procedures
|
61
|
+
@procedures = []
|
62
|
+
select_mysql(:proc, :type => 'PROCEDURE') do |h|
|
63
|
+
@procedures << Procedure.new(h)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize_views
|
68
|
+
@views = []
|
69
|
+
select_is(:views, :table_schema => self.name) do |h|
|
70
|
+
@views << View.new(h)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
37
74
|
def initialize_tables
|
38
75
|
@tables = []
|
39
|
-
select_is(:tables, :table_schema => self.name) do |h|
|
76
|
+
select_is(:tables, :table_schema => self.name, :table_type => 'BASE TABLE') do |h|
|
40
77
|
t = Table.new(h)
|
41
78
|
t.charset = charset(t.collation)
|
42
79
|
|
@@ -58,7 +95,9 @@ class DbDiff
|
|
58
95
|
|
59
96
|
select_is(:columns) do |h|
|
60
97
|
c = Column.new(h)
|
61
|
-
table(c.table_name)
|
98
|
+
t = table(c.table_name)
|
99
|
+
# we may not have the table since we get view columns here
|
100
|
+
t.columns << c if t
|
62
101
|
end
|
63
102
|
end
|
64
103
|
|
@@ -97,11 +136,18 @@ class DbDiff
|
|
97
136
|
end
|
98
137
|
end
|
99
138
|
|
139
|
+
def select_mysql(table, where = {}, &block)
|
140
|
+
@dbh.select_db('mysql')
|
141
|
+
# XXX
|
142
|
+
where[:db] = self.name
|
143
|
+
self._select(table, where, &block)
|
144
|
+
end
|
145
|
+
|
100
146
|
def select_is(table, where = {}, &block)
|
101
147
|
@dbh.select_db('information_schema')
|
102
148
|
|
103
149
|
# XXX
|
104
|
-
where[:table_schema] = self.name unless table == :character_sets
|
150
|
+
where[:table_schema] = self.name unless table == :character_sets || table == :triggers
|
105
151
|
|
106
152
|
self._select(table, where, &block)
|
107
153
|
end
|
data/lib/dbdiff/delta.rb
CHANGED
@@ -213,6 +213,7 @@ class DbDiff
|
|
213
213
|
super
|
214
214
|
# we don't clone foreign_keys or rows since
|
215
215
|
# the table_elements diff will take care of them
|
216
|
+
@element.triggers = []
|
216
217
|
@element.rows = []
|
217
218
|
@element.foreign_keys = []
|
218
219
|
end
|
@@ -260,6 +261,94 @@ class DbDiff
|
|
260
261
|
database.tables.delete(element)
|
261
262
|
end
|
262
263
|
end
|
264
|
+
|
265
|
+
class AddView < Delta
|
266
|
+
|
267
|
+
def sql
|
268
|
+
return "CREATE VIEW `%s` AS (%s)" % [element.name,element.definition]
|
269
|
+
end
|
270
|
+
|
271
|
+
def process(database)
|
272
|
+
database.views << element
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
class DropView < Delta
|
277
|
+
|
278
|
+
def sql
|
279
|
+
return "DROP VIEW `%s`" % element.name
|
280
|
+
end
|
281
|
+
|
282
|
+
def process(database)
|
283
|
+
database.views.delete(element)
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
class AddFunction < Delta
|
289
|
+
|
290
|
+
def sql
|
291
|
+
return "CREATE FUNCTION `#{element.name}` (#{element.param_list}) RETURNS #{element.returns} #{element.definition}"
|
292
|
+
end
|
293
|
+
|
294
|
+
def process(database)
|
295
|
+
database.functions << element
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class AddProcedure < Delta
|
300
|
+
|
301
|
+
def sql
|
302
|
+
return "CREATE PROCEDURE `#{element.name}` (#{element.param_list}) #{element.definition}"
|
303
|
+
end
|
304
|
+
|
305
|
+
def process(database)
|
306
|
+
database.procedures << element
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
class DropFunction < Delta
|
311
|
+
def sql
|
312
|
+
return "DROP FUNCTION `%s`" % element.name
|
313
|
+
end
|
314
|
+
|
315
|
+
def process(database)
|
316
|
+
database.functions.delete(element)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
class DropProcedure < Delta
|
321
|
+
def sql
|
322
|
+
return "DROP PROCEDURE `%s`" % element.name
|
323
|
+
end
|
324
|
+
|
325
|
+
def process(database)
|
326
|
+
database.procedures.delete(element)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
class AddTrigger < Delta
|
331
|
+
|
332
|
+
def sql
|
333
|
+
return "CREATE TRIGGER `#{element.name}` #{element.timing} #{element.event} ON #{element.table_name} FOR EACH ROW #{element.definition}"
|
334
|
+
end
|
335
|
+
|
336
|
+
def process(database)
|
337
|
+
table(database).triggers << element
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
class DropTrigger < Delta
|
342
|
+
|
343
|
+
def sql
|
344
|
+
return "DROP TRIGGER `#{element.name}`"
|
345
|
+
end
|
346
|
+
|
347
|
+
def process(database)
|
348
|
+
table(database).triggers.delete(element)
|
349
|
+
end
|
350
|
+
|
351
|
+
end
|
263
352
|
end
|
264
353
|
end
|
265
354
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class DbDiff
|
2
|
+
class Routine
|
3
|
+
attr_reader :name, :param_list, :definition, :returns, :routine_type, :comment
|
4
|
+
|
5
|
+
def initialize(info= {})
|
6
|
+
@name = info['name']
|
7
|
+
@definition = info['body']
|
8
|
+
@param_list = info['param_list']
|
9
|
+
@routine_type = info['type']
|
10
|
+
@returns = info['returns']
|
11
|
+
@comment = info['comment']
|
12
|
+
end
|
13
|
+
|
14
|
+
def modify_delta(new_element)
|
15
|
+
[self.drop_delta, new_element.add_delta]
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
self.name == other.name &&
|
20
|
+
self.param_list == other.param_list &&
|
21
|
+
self.routine_type == other.routine_type &&
|
22
|
+
self.returns == other.returns &&
|
23
|
+
self.definition == other.definition
|
24
|
+
end
|
25
|
+
|
26
|
+
def deep_clone
|
27
|
+
Marshal::load(Marshal.dump(self))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/dbdiff/row.rb
CHANGED
@@ -26,7 +26,15 @@ class DbDiff
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def ==(other)
|
29
|
-
|
29
|
+
other_data = other.data.dup
|
30
|
+
source_data = self.data.dup
|
31
|
+
|
32
|
+
%w(created_on updated_on).each do|c|
|
33
|
+
other_data.delete(c)
|
34
|
+
source_data.delete(c)
|
35
|
+
end
|
36
|
+
|
37
|
+
source_data == other_data
|
30
38
|
end
|
31
39
|
end
|
32
40
|
end
|
data/lib/dbdiff/table.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
class DbDiff
|
2
2
|
class Table
|
3
3
|
attr_reader :name
|
4
|
-
attr_accessor :charset, :rows, :keys, :foreign_keys, :columns, :collation, :engine
|
4
|
+
attr_accessor :charset, :rows, :triggers, :keys, :foreign_keys, :columns, :collation, :engine
|
5
5
|
|
6
6
|
def initialize(info = {})
|
7
|
-
@data = {}
|
8
7
|
|
9
8
|
@rows = []
|
10
9
|
@foreign_keys = []
|
11
10
|
@keys = []
|
12
11
|
@columns = []
|
12
|
+
@triggers = []
|
13
13
|
|
14
14
|
@engine = info['ENGINE']
|
15
15
|
@collation = info['TABLE_COLLATION']
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'dbdiff/routine'
|
2
|
+
class DbDiff
|
3
|
+
class Trigger < TableElement
|
4
|
+
attr_reader :definition, :timing, :event
|
5
|
+
# XXX may want dtd_identifier
|
6
|
+
#
|
7
|
+
def initialize(info = {})
|
8
|
+
@name = info['TRIGGER_NAME']
|
9
|
+
@table_name = info['EVENT_OBJECT_TABLE']
|
10
|
+
@definition = info['ACTION_STATEMENT']
|
11
|
+
@timing = info['ACTION_TIMING']
|
12
|
+
@event = info['EVENT_MANIPULATION']
|
13
|
+
end
|
14
|
+
|
15
|
+
def modify_delta(new_element)
|
16
|
+
[self.drop_delta, new_element.add_delta]
|
17
|
+
end
|
18
|
+
|
19
|
+
def drop_delta
|
20
|
+
Delta::DropTrigger.new(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_delta
|
24
|
+
Delta::AddTrigger.new(self)
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(other)
|
28
|
+
self.name == other.name &&
|
29
|
+
self.definition == other.definition &&
|
30
|
+
self.table_name == other.table_name &&
|
31
|
+
self.timing == other.timing &&
|
32
|
+
self.event == other.event
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/dbdiff/view.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class DbDiff
|
2
|
+
class View
|
3
|
+
attr_reader :name, :definition
|
4
|
+
|
5
|
+
def initialize(info = {})
|
6
|
+
@definition = info['VIEW_DEFINITION']
|
7
|
+
@name = info['TABLE_NAME']
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
self.definition == other.definition
|
13
|
+
end
|
14
|
+
|
15
|
+
def deep_clone
|
16
|
+
t = Marshal::load(Marshal.dump(self))
|
17
|
+
# must clear rows out since they won't necessary be in the copy
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_delta
|
21
|
+
Delta::AddView.new(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def modify_delta(new_element)
|
25
|
+
[self.drop_delta, new_element.add_delta]
|
26
|
+
end
|
27
|
+
|
28
|
+
def drop_delta
|
29
|
+
Delta::DropView.new(self)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
CREATE TABLE authors (
|
2
|
+
`id` int(11) NOT NULL auto_increment,
|
3
|
+
`name` char(60) NOT NULL,
|
4
|
+
`address` char(60) NULL,
|
5
|
+
PRIMARY KEY (`id`)
|
6
|
+
) ENGINE=innodb DEFAULT CHARSET=latin1;
|
7
|
+
|
8
|
+
CREATE PROCEDURE simpleproc (OUT param1 INT) BEGIN SELECT COUNT(*) INTO param1 FROM authors WHERE id = 2; END;
|
@@ -0,0 +1,9 @@
|
|
1
|
+
CREATE TABLE authors (
|
2
|
+
`id` int(11) NOT NULL auto_increment,
|
3
|
+
`name` char(60) NOT NULL,
|
4
|
+
`address` char(60) NULL,
|
5
|
+
PRIMARY KEY (`id`)
|
6
|
+
) ENGINE=innodb DEFAULT CHARSET=latin1;
|
7
|
+
|
8
|
+
CREATE PROCEDURE simpleproc (OUT param1 INT) BEGIN SELECT COUNT(*) INTO param1 FROM authors; END;
|
9
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
CREATE TABLE authors (
|
2
|
+
`id` int(11) NOT NULL auto_increment,
|
3
|
+
`name` char(60) NOT NULL,
|
4
|
+
`address` char(60) NULL,
|
5
|
+
PRIMARY KEY (`id`)
|
6
|
+
) ENGINE=innodb DEFAULT CHARSET=latin1;
|
7
|
+
|
8
|
+
create trigger my_auth BEFORE INSERT on authors FOR EACH ROW BEGIN SET NEW.name = 'fXXXX'; END;
|
9
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
CREATE TABLE authors (
|
2
|
+
`id` int(11) NOT NULL auto_increment,
|
3
|
+
`name` char(60) NOT NULL,
|
4
|
+
`address` char(60) NULL,
|
5
|
+
PRIMARY KEY (`id`)
|
6
|
+
) ENGINE=innodb DEFAULT CHARSET=latin1;
|
7
|
+
|
8
|
+
create trigger my_auth BEFORE INSERT on authors FOR EACH ROW BEGIN SET NEW.name = 'fOO'; END;
|
9
|
+
|