dbdiff 0.1.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.
Files changed (58) hide show
  1. data/CHANGELOG +6 -0
  2. data/LICENSE +58 -0
  3. data/README +29 -0
  4. data/lib/dbdiff/column.rb +69 -0
  5. data/lib/dbdiff/database.rb +169 -0
  6. data/lib/dbdiff/delta.rb +266 -0
  7. data/lib/dbdiff/foreign_key.rb +68 -0
  8. data/lib/dbdiff/key.rb +69 -0
  9. data/lib/dbdiff/row.rb +32 -0
  10. data/lib/dbdiff/table.rb +50 -0
  11. data/lib/dbdiff/table_element.rb +16 -0
  12. data/lib/dbdiff.rb +207 -0
  13. data/test/ai_column/source.sql +10 -0
  14. data/test/ai_column/target.sql +7 -0
  15. data/test/change_pk/source.sql +11 -0
  16. data/test/change_pk/target.sql +8 -0
  17. data/test/column/source.sql +10 -0
  18. data/test/column/target.sql +9 -0
  19. data/test/fk/source.sql +19 -0
  20. data/test/fk/target.sql +20 -0
  21. data/test/key/source.sql +11 -0
  22. data/test/key/target.sql +10 -0
  23. data/test/modify_column/source.sql +9 -0
  24. data/test/modify_column/target.sql +10 -0
  25. data/test/modify_fk/source.sql +20 -0
  26. data/test/modify_fk/target.sql +22 -0
  27. data/test/modify_key_fk/source.sql +19 -0
  28. data/test/modify_key_fk/target.sql +20 -0
  29. data/test/modify_key_fk_ref/source.sql +18 -0
  30. data/test/modify_key_fk_ref/target.sql +20 -0
  31. data/test/modify_row/source.sql +8 -0
  32. data/test/modify_row/target.sql +9 -0
  33. data/test/modify_table/source.sql +8 -0
  34. data/test/modify_table/target.sql +9 -0
  35. data/test/multi_fk/source.sql +22 -0
  36. data/test/multi_fk/target.sql +20 -0
  37. data/test/multi_key/source.sql +11 -0
  38. data/test/multi_key/target.sql +10 -0
  39. data/test/multi_unique_key/source.sql +11 -0
  40. data/test/multi_unique_key/target.sql +10 -0
  41. data/test/row/source.sql +8 -0
  42. data/test/row/target.sql +6 -0
  43. data/test/suite.rb +7 -0
  44. data/test/table/source.sql +9 -0
  45. data/test/table/target.sql +0 -0
  46. data/test/table_fk/source.sql +13 -0
  47. data/test/table_fk/target.sql +20 -0
  48. data/test/table_fk2/source.sql +17 -0
  49. data/test/table_fk2/target.sql +5 -0
  50. data/test/test_column.rb +93 -0
  51. data/test/test_dbdiff.rb +92 -0
  52. data/test/test_foreign_key.rb +136 -0
  53. data/test/test_key.rb +116 -0
  54. data/test/test_row.rb +84 -0
  55. data/test/test_table.rb +30 -0
  56. data/test/unique_key/source.sql +11 -0
  57. data/test/unique_key/target.sql +9 -0
  58. metadata +127 -0
data/lib/dbdiff/key.rb ADDED
@@ -0,0 +1,69 @@
1
+ class DbDiff
2
+ class Key < TableElement
3
+ attr_reader :column_data, :unique, :primary
4
+
5
+ def initialize(table_name, info= {})
6
+ @table_name = table_name
7
+
8
+
9
+ # XXX need to work on tihs
10
+ @name = info['Key_name']
11
+
12
+ @primary = (@name == 'PRIMARY' ? true : false)
13
+ seq_index = info['Seq_in_index'].to_i - 1
14
+ @unique = (info['Non_unique'].to_i == 1 ? false : true)
15
+ @column_data = {}
16
+ @column_data[seq_index] = info['Column_name']
17
+ end
18
+
19
+ def columns
20
+ @column_data.keys.sort.map {|k| @column_data[k]}
21
+ end
22
+
23
+ def merge(key)
24
+
25
+ unless key.name == self.name
26
+ raise "Error - names don't match #{key.name} #{self.name}"
27
+ end
28
+
29
+ @column_data.merge!(key.column_data)
30
+ end
31
+
32
+ def definition
33
+
34
+ sql = ""
35
+ if @primary
36
+ sql += " PRIMARY KEY "
37
+ elsif @unique
38
+ sql += " UNIQUE KEY `%s`" % self.name
39
+ else
40
+ sql += " KEY `%s`" % self.name
41
+ end
42
+
43
+ sql += " (" + self.columns.map{|c| '`' + c + '`' }.join(",") + " )"
44
+
45
+ sql
46
+ end
47
+
48
+ def modify_delta(new_element)
49
+ Delta::ModifyKey.new(new_element)
50
+ end
51
+
52
+ def drop_delta
53
+ Delta::DropKey.new(self)
54
+ end
55
+
56
+ def add_delta
57
+ Delta::AddKey.new(self)
58
+ end
59
+
60
+ def ==(other)
61
+ self.columns == other.columns &&
62
+ self.table_name == other.table_name &&
63
+ self.primary == other.primary &&
64
+ self.unique == other.unique &&
65
+ self.name == other.name
66
+ end
67
+
68
+ end
69
+ end
data/lib/dbdiff/row.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'dbdiff/table_element'
2
+
3
+ class DbDiff
4
+
5
+ class Row < TableElement
6
+ attr_reader :primary_key, :data
7
+
8
+ def initialize(table, info = {})
9
+ @data = info.dup
10
+ @primary_key = table.primary_key
11
+ @name = @primary_key.map {|k|@data[k] }.join("|") # XXX fix this to be more unique of a name
12
+ @table_name = table.name
13
+ end
14
+
15
+
16
+ def add_delta
17
+ Delta::AddRow.new(self)
18
+ end
19
+
20
+ def modify_delta(new_element)
21
+ Delta::ModifyRow.new(new_element)
22
+ end
23
+
24
+ def drop_delta
25
+ Delta::DropRow.new(self)
26
+ end
27
+
28
+ def ==(other)
29
+ self.data == other.data
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ class DbDiff
2
+ class Table
3
+ attr_reader :name
4
+ attr_accessor :charset, :rows, :keys, :foreign_keys, :columns, :collation, :engine
5
+
6
+ def initialize(info = {})
7
+ @data = {}
8
+
9
+ @rows = []
10
+ @foreign_keys = []
11
+ @keys = []
12
+ @columns = []
13
+
14
+ @engine = info['ENGINE']
15
+ @collation = info['TABLE_COLLATION']
16
+ @name = info['TABLE_NAME']
17
+ end
18
+
19
+ alias :table_name :name
20
+
21
+ def primary_key
22
+ pk = keys.find{|k| k.primary}
23
+ return (pk ? pk.columns : nil)
24
+ end
25
+
26
+ def ==(other)
27
+ self.engine == other.engine &&
28
+ self.collation == other.collation &&
29
+ self.name == other.name
30
+ end
31
+
32
+ def deep_clone
33
+ t = Marshal::load(Marshal.dump(self))
34
+ # must clear rows out since they won't necessary be in the copy
35
+ end
36
+
37
+ def add_delta
38
+ Delta::AddTable.new(self)
39
+ end
40
+
41
+ def modify_delta(new_element)
42
+ Delta::ModifyTable.new(new_element)
43
+ end
44
+
45
+ def drop_delta
46
+ Delta::DropTable.new(self)
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,16 @@
1
+ class DbDiff
2
+ class TableElement
3
+ attr_reader :name, :table_name
4
+
5
+ def initialize(info = {})
6
+ @table_name = info['TABLE_NAME']
7
+ end
8
+
9
+
10
+ def deep_clone
11
+ Marshal::load(Marshal.dump(self))
12
+ end
13
+
14
+ end
15
+ end
16
+
data/lib/dbdiff.rb ADDED
@@ -0,0 +1,207 @@
1
+ require 'dbdiff/database'
2
+ require 'logger'
3
+
4
+ # = Synopsis
5
+ # DbDiff allows you to compare two different databases (currently MySQL),
6
+ # determine the differences between the two, and apply the deltas.
7
+ # Currently the following types of deltas are supported: tables,
8
+ # columns, keys, foreign keys and rows.
9
+ #
10
+ # Specifying tables in the constructor causes these tables to be
11
+ # replicated.
12
+ #
13
+ # Nothing in dropped or removed unless you explicitly pass :drop_row,
14
+ # :drop_column or :drop_table.
15
+ #
16
+ # By default, calling apply_diffs does nothing - you must explicitly
17
+ # set dry_run to false on the dbdiff object.
18
+ #
19
+ #
20
+ # == Example
21
+ # require 'rubygems'
22
+ # require 'dbdiff'
23
+ #
24
+ # dbdiff = DbDiff.new( {:host => 127.0.0.1, :password => nil, :user => 'root', :name => 'source_db'},
25
+ # {:host => 127.0.0.1, :password => nil, :user => 'root', :name => 'target_db'},
26
+ # :logger => Logger.new(STDOUT),
27
+ # :tables => %w(authors books)
28
+ # )
29
+ # # don't actually execute any SQL against the target
30
+ # dbdiff.dry_run = true
31
+ #
32
+ # # perform diff between source and target
33
+ # dbiff.diff
34
+ #
35
+ # # process diffs - arguments indicate that drop_column, drop_row (only for replicated tables),
36
+ # # and drop_table are allowed within diffs
37
+ # # are allowed
38
+ # diff.apply_diffs(:drop_column, :drop_row, :drop_table)
39
+ #
40
+
41
+ class DbDiff
42
+ attr_reader :source, :target, :tables, :logger
43
+ attr_accessor :dry_run
44
+
45
+ # Creates a new dbdiff object with the connect strings s/t (source/target)
46
+ def initialize(s, t, params = {})
47
+ @source = DbDiff::Database.new(s)
48
+ @target = DbDiff::Database.new(t)
49
+ @tables = params[:tables] || []
50
+ @logger = params[:logger] || Logger.new(nil)
51
+
52
+ @source.load_rows(@tables)
53
+ @target.load_rows(@tables)
54
+ @dry_run = true
55
+ end
56
+
57
+ # Compare tables and all elements (columns, rows, keys, foreign keys)
58
+ # differences are stored on the deltas attribute of the target
59
+ # database
60
+ def diff
61
+ compare_tables
62
+ compare_elements(:columns, :rows, :keys, :foreign_keys)
63
+ end
64
+
65
+ # Apply deltas stored on the target
66
+ # all SQL executed will be sent to the logger as info messages
67
+ def apply_diffs (*allowed_deltas)
68
+
69
+
70
+ # these deltas must be manually
71
+ # resolved or explicitly allowed
72
+ confirm_deltas = {
73
+ :drop_row => DbDiff::Delta::DropRow,
74
+ :drop_column => DbDiff::Delta::DropColumn,
75
+ :drop_table => DbDiff::Delta::DropTable
76
+ }
77
+
78
+ confirm_deltas = confirm_deltas.delete_if {|k,v| allowed_deltas.include?(k)}
79
+
80
+
81
+ delta_order = [
82
+ DbDiff::Delta::AddTable,
83
+ DbDiff::Delta::ModifyTable,
84
+ DbDiff::Delta::DropForeignKey,
85
+ DbDiff::Delta::DropTable, # can only drop a table after foreign keys have been dropped
86
+
87
+ DbDiff::Delta::AddColumn,
88
+
89
+ DbDiff::Delta::ModifyColumnRemoveAI, # ai must be removed before we can drop/modify a pk
90
+
91
+ DbDiff::Delta::ModifyKey, # must have columns exist prior to adding a key
92
+ DbDiff::Delta::AddKey, # must have new keys before old ones can be dropped
93
+ # to support FKs
94
+
95
+ DbDiff::Delta::DropKey, # must have FK dropped before we drop a key
96
+
97
+ DbDiff::Delta::ModifyColumnAddAI, # ai must be added before we can add a pk
98
+
99
+ DbDiff::Delta::DropRow,
100
+ DbDiff::Delta::ModifyColumn,
101
+
102
+ DbDiff::Delta::AddForeignKey,
103
+ DbDiff::Delta::DropColumn,
104
+ DbDiff::Delta::AddRow,
105
+ DbDiff::Delta::ModifyRow
106
+ ]
107
+
108
+ drop_deltas = target.deltas.find_all {|d| confirm_deltas.values.include?(d.class) }
109
+
110
+ if drop_deltas && drop_deltas.size > 0
111
+ message = "The following deltas must be either handled by a rename or explicitly allowed"
112
+ drop_deltas.each do|d|
113
+ message += "\n #{d.class.name}: " + d.sql
114
+ end
115
+ raise message
116
+ end
117
+
118
+ target.dbh.query("SET FOREIGN_KEY_CHECKS = 0")
119
+
120
+ target.deltas.sort_by {|d| delta_order.index(d.class) }.each do |d|
121
+ logger.info("processing #{d.sql}")
122
+ d.process(target)
123
+ run_sql(d.sql) unless @dry_run
124
+ end
125
+
126
+ target.dbh.query("SET FOREIGN_KEY_CHECKS = 1")
127
+
128
+ target.deltas = []
129
+ end
130
+
131
+ private
132
+
133
+ def run_sql(sql)
134
+ begin
135
+ target.dbh.select_db(target.name)
136
+ logger.info("executing #{sql}")
137
+ result = target.dbh.query(sql)
138
+ rescue => e
139
+ raise "error executing query '#{sql}': #{e}"
140
+ end
141
+ end
142
+
143
+ def compare_elements(*elements)
144
+
145
+ elements.each do |e|
146
+ diff_table_elements(source, target, e, :diff_add_modify)
147
+ diff_table_elements(target, source, e, :diff_drop)
148
+ end
149
+ end
150
+
151
+ def compare_tables
152
+
153
+ # only handle modify/add
154
+ source.tables.each do |t|
155
+ # XXX add authors2
156
+ diff_add_modify(source, target, t, target.tables.find{|x| x.name == t.name})
157
+ end
158
+
159
+ ## only handle removals here
160
+ target.tables.each do |t|
161
+ # xxx drop authors
162
+ diff_drop(target, source, t, source.tables.find{|x| x.name == t.name})
163
+ end
164
+
165
+ end
166
+
167
+ def diff_drop(a, b, element, target_element)
168
+ if !target_element
169
+ # source is actually the target database
170
+ logger.info("dropping #{element.class.name} #{element.name} #{element.table_name} from #{a.class.name} #{a.name}")
171
+ a.drop(element)
172
+ end
173
+ end
174
+
175
+ def diff_add_modify(a, b, element, target_element)
176
+
177
+ if !target_element
178
+ logger.info("adding element #{element.class.name} #{element.name} #{element.table_name} > #{b.name}")
179
+ b.add(element)
180
+ elsif target_element != element
181
+ logger.info("modifying element #{element.class.name} #{element.name} #{element.table_name} > #{b.name}")
182
+ b.modify(target_element, element)
183
+ end
184
+
185
+ end
186
+
187
+ def diff_table_elements(a, b, table_method, diff_method)
188
+
189
+
190
+ # all_tables allows us to add the FKs after adding the tables
191
+ # otherwise the table won't be there to add the FK
192
+ a.all_tables.each do |at|
193
+ bt = b.all_tables.find{|z| z.name == at.name}
194
+ # XXX possibly skip this and make the add check determine whether
195
+ # we can skip the add because we have already encountered the add
196
+ next unless bt
197
+
198
+ at.send(table_method).each do |ae|
199
+ be = bt.send(table_method).find{|x| x.name == ae.name}
200
+ send(diff_method, a, b, ae, be)
201
+ end
202
+ end
203
+
204
+ end
205
+
206
+ end
207
+
@@ -0,0 +1,10 @@
1
+ DROP TABLE IF EXISTS `authors`;
2
+
3
+ CREATE TABLE authors (
4
+ `id` int(11) NOT NULL auto_increment,
5
+ `name` char(60) NOT NULL,
6
+ `address1` char(60) NOT NULL,
7
+ PRIMARY KEY (`id`)
8
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
9
+
10
+
@@ -0,0 +1,7 @@
1
+
2
+ CREATE TABLE authors (
3
+ `name` char(60) NOT NULL,
4
+ `address1` char(60) NOT NULL
5
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
6
+
7
+
@@ -0,0 +1,11 @@
1
+ CREATE TABLE authors (
2
+ dim_id int not null,
3
+ res_id int not null,
4
+ name char(60) NOT NULL,
5
+ address1 char(60) NOT NULL,
6
+ PRIMARY KEY (`dim_id`, `res_id`)
7
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
8
+
9
+
10
+
11
+
@@ -0,0 +1,8 @@
1
+ CREATE TABLE authors (
2
+ id int(11) NOT NULL auto_increment,
3
+ dim_id int not null,
4
+ res_id int not null,
5
+ name char(60) NOT NULL,
6
+ address1 char(60) NOT NULL,
7
+ PRIMARY KEY (`id`)
8
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
@@ -0,0 +1,10 @@
1
+ DROP TABLE IF EXISTS `authors`;
2
+
3
+ CREATE TABLE authors (
4
+ `id` int(11) NOT NULL auto_increment,
5
+ `name` char(60) NOT NULL,
6
+ `address1` char(60) NOT NULL,
7
+ PRIMARY KEY (`id`)
8
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
9
+
10
+
@@ -0,0 +1,9 @@
1
+ DROP TABLE IF EXISTS `authors`;
2
+
3
+ CREATE TABLE authors (
4
+ `id` int(11) NOT NULL auto_increment,
5
+ `name` char(60) NOT NULL,
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
8
+
9
+
@@ -0,0 +1,19 @@
1
+
2
+ CREATE TABLE authors (
3
+ `id` int(11) NOT NULL auto_increment,
4
+ `name` char(60) NOT NULL,
5
+ `address1` char(60) NOT NULL,
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
8
+
9
+ CREATE TABLE books (
10
+ id int(11) NOT NULL auto_increment,
11
+ author_id int not null,
12
+ name char(60) NOT NULL,
13
+ pages int not null,
14
+ FOREIGN KEY (author_id) references authors(id),
15
+ PRIMARY KEY (`id`)
16
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
17
+
18
+
19
+
@@ -0,0 +1,20 @@
1
+ DROP TABLE IF EXISTS `authors`;
2
+
3
+ CREATE TABLE authors (
4
+ `id` int(11) NOT NULL auto_increment,
5
+ `name` char(60) NOT NULL,
6
+ `address1` char(60) NOT NULL,
7
+ PRIMARY KEY (`id`)
8
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
9
+
10
+ CREATE TABLE books (
11
+ id int(11) NOT NULL auto_increment,
12
+ author_id int not null,
13
+ name char(60) NOT NULL,
14
+ pages int not null,
15
+ PRIMARY KEY (`id`)
16
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
17
+
18
+
19
+
20
+
@@ -0,0 +1,11 @@
1
+ DROP TABLE IF EXISTS `authors`;
2
+
3
+ CREATE TABLE authors (
4
+ `id` int(11) NOT NULL auto_increment,
5
+ `name` char(60) NOT NULL,
6
+ `address1` char(60) NOT NULL,
7
+ index(name),
8
+ PRIMARY KEY (`id`)
9
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
10
+
11
+
@@ -0,0 +1,10 @@
1
+ DROP TABLE IF EXISTS `authors`;
2
+
3
+ CREATE TABLE authors (
4
+ `id` int(11) NOT NULL auto_increment,
5
+ `name` char(60) NOT NULL,
6
+ `address1` char(60) NOT NULL,
7
+ PRIMARY KEY (`id`)
8
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
9
+
10
+
@@ -0,0 +1,9 @@
1
+
2
+ CREATE TABLE authors (
3
+ `id` int(11) NOT NULL auto_increment,
4
+ `name` char(60) NOT NULL,
5
+ `address1` char(60) NOT NULL,
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
8
+
9
+
@@ -0,0 +1,10 @@
1
+ DROP TABLE IF EXISTS `authors`;
2
+
3
+ CREATE TABLE authors (
4
+ `id` int(11) NOT NULL auto_increment,
5
+ `name` char(60) NOT NULL,
6
+ `address1` char(40) NOT NULL,
7
+ PRIMARY KEY (`id`)
8
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
9
+
10
+
@@ -0,0 +1,20 @@
1
+
2
+ CREATE TABLE authors (
3
+ `id` int(11) NOT NULL auto_increment,
4
+ `name` char(60) NOT NULL,
5
+ `address1` char(60) NOT NULL,
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
8
+
9
+ CREATE TABLE books (
10
+ id int(11) NOT NULL auto_increment,
11
+ author_id int not null,
12
+ new_id int not null,
13
+ name char(60) NOT NULL,
14
+ pages int not null,
15
+ CONSTRAINT `author_fk1` FOREIGN KEY (new_id) references authors(id),
16
+ PRIMARY KEY (`id`)
17
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
18
+
19
+
20
+
@@ -0,0 +1,22 @@
1
+ DROP TABLE IF EXISTS `authors`;
2
+
3
+ CREATE TABLE authors (
4
+ `id` int(11) NOT NULL auto_increment,
5
+ `name` char(60) NOT NULL,
6
+ `address1` char(60) NOT NULL,
7
+ PRIMARY KEY (`id`)
8
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
9
+
10
+ CREATE TABLE books (
11
+ id int(11) NOT NULL auto_increment,
12
+ author_id int not null,
13
+ new_id int not null,
14
+ name char(60) NOT NULL,
15
+ pages int not null,
16
+ CONSTRAINT `author_fk1` FOREIGN KEY (author_id) references authors(id),
17
+ PRIMARY KEY (`id`)
18
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
19
+
20
+
21
+
22
+
@@ -0,0 +1,19 @@
1
+
2
+ CREATE TABLE authors (
3
+ id int(11) NOT NULL auto_increment,
4
+ name char(60) NOT NULL,
5
+ address1 char(60) NOT NULL,
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
8
+
9
+ CREATE TABLE books (
10
+ id int(11) NOT NULL auto_increment,
11
+ author_id int not null,
12
+ name char(60) NOT NULL,
13
+ unique(author_id),
14
+ FOREIGN KEY (author_id) references authors(id),
15
+ PRIMARY KEY (`id`)
16
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
17
+
18
+
19
+
@@ -0,0 +1,20 @@
1
+
2
+ CREATE TABLE authors (
3
+ id int(11) NOT NULL auto_increment,
4
+ name char(60) NOT NULL,
5
+ address1 char(60) NOT NULL,
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
8
+
9
+ CREATE TABLE books (
10
+ id int(11) NOT NULL auto_increment,
11
+ author_id int not null,
12
+ name char(60) NOT NULL,
13
+ index(author_id),
14
+ FOREIGN KEY (author_id) references authors(id),
15
+ PRIMARY KEY (`id`)
16
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
17
+
18
+
19
+
20
+
@@ -0,0 +1,18 @@
1
+
2
+ CREATE TABLE authors (
3
+ id int(11) NOT NULL auto_increment,
4
+ name char(60) NOT NULL,
5
+ address1 char(60) NOT NULL,
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
8
+
9
+ CREATE TABLE books (
10
+ id int(11) NOT NULL auto_increment,
11
+ author_id int not null,
12
+ address_id int not null,
13
+ name char(60) NOT NULL,
14
+ unique(author_id),
15
+ FOREIGN KEY (author_id) references authors(id),
16
+ PRIMARY KEY (`id`)
17
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
18
+
@@ -0,0 +1,20 @@
1
+
2
+ CREATE TABLE authors (
3
+ id int(11) NOT NULL auto_increment,
4
+ name char(60) NOT NULL,
5
+ address1 char(60) NOT NULL,
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
8
+
9
+ CREATE TABLE books (
10
+ id int(11) NOT NULL auto_increment,
11
+ author_id int not null,
12
+ name char(60) NOT NULL,
13
+ index(author_id),
14
+ FOREIGN KEY (author_id) references authors(id),
15
+ PRIMARY KEY (`id`)
16
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
17
+
18
+
19
+
20
+
@@ -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
+ INSERT INTO authors (id, name,address) values (1, 'FOO', NULL);
@@ -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
+ INSERT INTO authors (id, name, address) values (1, 'BAR', 'BLAh');
9
+
@@ -0,0 +1,8 @@
1
+
2
+ CREATE TABLE authors (
3
+ `id` int(11) NOT NULL auto_increment,
4
+ `name` char(60) NOT NULL,
5
+ PRIMARY KEY (`id`)
6
+ ) ENGINE=innodb DEFAULT CHARSET=latin1;
7
+
8
+