mysql2mysql 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.
data/Changelog ADDED
@@ -0,0 +1,2 @@
1
+ v0.0.1
2
+ first release
data/INSTALL ADDED
@@ -0,0 +1,9 @@
1
+ [sudo] gem install mysql2mysql
2
+
3
+ $ irb
4
+ > require 'rubygems'
5
+ true
6
+ > require 'mysql2mysql'
7
+ true
8
+ > Mysql2Mysql.version
9
+ 0.0.1
data/README.rdoc ADDED
@@ -0,0 +1,20 @@
1
+ == Description
2
+
3
+ dump table's structure and data between mysql servers and databases.
4
+
5
+ == Example
6
+ require 'rubygems'
7
+ require 'mysql2mysql'
8
+ Mysql2Mysql.new.
9
+ from('mysql://mysql.server1/test?user=root&password=pass').
10
+ to('mysql://mysql.server2/test?user=root&password=pass').
11
+ tables('test' => :all, 'mysql' => /^time_zone/).
12
+ dump(:charset => 'utf8', 'with_data' => true)
13
+
14
+ == Version
15
+
16
+ v0.0.1
17
+
18
+ == Author
19
+
20
+ xianhua.zhou<xianhua.zhou@gmail.com>
@@ -0,0 +1 @@
1
+ require 'mysql_2_mysql'
@@ -0,0 +1,277 @@
1
+ #
2
+ # == Description
3
+ #
4
+ # dump table's structure and data between mysql servers and databases.
5
+ #
6
+ # == Example
7
+ #
8
+ # Please read README.rdoc
9
+ #
10
+ # == Version
11
+ #
12
+ # v0.0.1
13
+ #
14
+ # == Author
15
+ #
16
+ # xianhua.zhou<xianhua.zhou@gmail.com>
17
+ #
18
+ require 'sequel'
19
+
20
+ class Mysql2Mysql
21
+
22
+ VERSION = '0.0.1'
23
+
24
+ def self.version
25
+ VERSION
26
+ end
27
+
28
+ @@methods = %w(from to tables exclude)
29
+
30
+ def initialize(opts = {})
31
+ @@methods.each do |method_name|
32
+ send method_name, opts[method_name.to_sym] or opts[method_name.to_s]
33
+ end
34
+ end
35
+
36
+ @@methods.each do |method_name|
37
+ class_eval %Q{
38
+ def #{method_name}(#{method_name})
39
+ @#{method_name} = #{method_name}
40
+ self
41
+ end
42
+ }
43
+ end
44
+
45
+ def dump(opts = {})
46
+ # initialize database connections
47
+ init_db_connection
48
+
49
+ # initialize opts
50
+ opts = dump_opts opts
51
+
52
+ # before all callback
53
+ before_dump opts
54
+
55
+ tables_list.each do |database, tables|
56
+ tables.each do |table|
57
+
58
+ # before each callback
59
+ if opts[:before_each].respond_to? :call
60
+ to_database, to_table = opts[:before_each].call(database, table)
61
+ end
62
+ to_database ||= database
63
+ to_table ||= table
64
+
65
+ dump_table database, table, to_database, to_table, opts
66
+
67
+ # after each callback
68
+ if opts[:after_each].respond_to? :call
69
+ opts[:after_each].call(database, table)
70
+ end
71
+ end
72
+ end
73
+
74
+ # after all callback
75
+ after_dump opts
76
+
77
+ end
78
+
79
+ private
80
+
81
+ def dump_opts(opts)
82
+ {
83
+ # it's used for "SET NAMES #{charset}"
84
+ :charset => 'utf8',
85
+
86
+ # dump data or just table structure
87
+ :with_data => true,
88
+
89
+ # drop the table before do dump the table
90
+ :drop_table_first => false,
91
+
92
+ # callbacks
93
+ :before_all => nil,
94
+ :after_all => nil,
95
+ :before_each => nil,
96
+ :after_each => nil
97
+ }.merge(opts)
98
+ end
99
+
100
+ def before_dump(opts)
101
+ # prepare dump
102
+ [
103
+ "SET NAMES #{opts[:charset]}",
104
+ "SET FOREIGN_KEY_CHECKS = 0",
105
+ "SET UNIQUE_CHECKS = 0"
106
+ ].each do |sql|
107
+ run_sql sql, :on_connection => @to_db
108
+ end
109
+ opts[:before_all].call(@from_db, @to_db) if opts[:after_all].respond_to? :call
110
+ end
111
+
112
+ def after_dump(opts)
113
+ # clean up
114
+ [
115
+ "SET FOREIGN_KEY_CHECKS = 1",
116
+ "SET UNIQUE_CHECKS = 1"
117
+ ].each do |sql|
118
+ run_sql sql, :on_connection => @to_db
119
+ end
120
+ opts[:after_all].call(@from_db, @to_db) if opts[:after_all].respond_to? :call
121
+ end
122
+
123
+ def dump_table(from_database, from_table, to_database, to_table, opts = {})
124
+ use_db from_database, :on_connection => @from_db
125
+ create_table_ddl = @from_db.fetch("SHOW CREATE TABLE #{from_table}").first[:'Create Table']
126
+
127
+ # create database
128
+ begin
129
+ use_db to_database, :on_connection => @to_db
130
+ rescue Sequel::DatabaseError => e
131
+ begin
132
+ run_sql "CREATE DATABASE #{to_database}", :on_connection => @to_db
133
+ use_db to_database, :on_connection => @to_db
134
+ rescue Exception => e
135
+ raise Mysql2MysqlException.new "create database #{to_database} failed\n DSN info: #{@to_db}"
136
+ end
137
+ end
138
+
139
+ # create table
140
+ unless @to_db.table_exists? to_table.to_sym
141
+ begin
142
+ run_sql create_table_ddl.gsub("`#{from_table}`", "`#{to_table}`"), :on_connection => @to_db
143
+ rescue Exception => e
144
+ raise Mysql2MysqlException.new "create table #{to_table} failed in the database #{to_database}\n DSN info: #{@to_db}\n: message: #{e}"
145
+ end
146
+ end
147
+
148
+ return unless opts[:with_data]
149
+
150
+ # dump data
151
+ @from_db.fetch("SELECT * FROM #{from_table}").each do |row|
152
+ @to_db[to_table.to_sym].insert row
153
+ end
154
+ end
155
+
156
+ def run_sql(sql, opts)
157
+ opts[:on_connection].run sql
158
+ end
159
+
160
+ def use_db(db, opts)
161
+ run_sql "use #{db}", opts
162
+ end
163
+
164
+ def init_db_connection
165
+ @from_db = db_connection @from
166
+ @to_db = db_connection @to
167
+ end
168
+
169
+ def db_connection(dsn)
170
+ dsn[:adapter] = 'mysql' if dsn.is_a? Hash
171
+ Sequel.connect(dsn)
172
+ end
173
+
174
+ def tables_list
175
+ raise Mysql2MysqlException.new 'No tables need to dump' if @tables.nil?
176
+ filter_tables
177
+ end
178
+
179
+ def all_databases
180
+ @from_db.fetch('SHOW DATABASES').collect do |row|
181
+ row[:Database]
182
+ end
183
+ end
184
+
185
+ def filter_tables
186
+ all_valid_tables = \
187
+ if is_all? @tables
188
+ all_databases.inject({}) do |all_tables, dbname|
189
+ all_tables.merge dbname => get_tables_by_db(dbname)
190
+ end
191
+ else
192
+ all_tables = {}
193
+ all_databases.each do |dbname|
194
+ @tables.each do |orig_dbname, orig_tbname|
195
+ next unless is_eql_or_match?(orig_dbname, dbname)
196
+
197
+ tables = get_tables_by_db dbname
198
+
199
+ if is_all? orig_tbname
200
+ all_tables[dbname] = tables
201
+ break
202
+ end
203
+
204
+ orig_tbnames = [orig_tbname] unless orig_tbname.is_a? Array
205
+ tables = tables.find_all do |tbname|
206
+ orig_tbnames.find do |orig_tbname|
207
+ is_eql_or_match?(orig_tbname, tbname)
208
+ end
209
+ end
210
+
211
+ all_tables[dbname] = tables
212
+ end
213
+ end
214
+
215
+ all_tables
216
+ end
217
+
218
+ filter all_valid_tables
219
+ end
220
+
221
+ def is_eql_or_match?(origin, current)
222
+ if origin.is_a? Regexp
223
+ origin.match current
224
+ else
225
+ origin.to_s == current.to_s
226
+ end
227
+ end
228
+
229
+ def is_all?(items)
230
+ [:all, '*'].include? items
231
+ end
232
+
233
+ def get_tables_by_db(dbname)
234
+ use_db dbname, :on_connection => @from_db
235
+ @from_db.tables
236
+ end
237
+
238
+ def filter(origin_tables)
239
+ need_exclude = lambda do |origin, exclude|
240
+ is_eql_or_match? origin.to_s, exclude
241
+ end
242
+
243
+ return origin_tables if @exclude.nil?
244
+
245
+ exclude_tables = case @exclude
246
+ when String
247
+ {@exclude => '*'}
248
+ when Array
249
+ @exclude.inject({}) do |items, it|
250
+ items[it] = '*'
251
+ end
252
+ when Hash
253
+ @exclude
254
+ else
255
+ raise Mysql2MysqlException.new 'Invalid exclude parameters given'
256
+ end
257
+
258
+ reject_action = lambda do |dbname, tbname|
259
+ exclude_tables.each do |exclude_dbname, exclude_tbname|
260
+ if need_exclude.call(dbname, exclude_dbname)
261
+ if is_all?(exclude_tbname) or need_exclude.call(tbname, exclude_tbname)
262
+ return true
263
+ end
264
+ end
265
+ end
266
+
267
+ false
268
+ end
269
+
270
+ origin_tables.reject do |dbname, tbname|
271
+ reject_action.call(dbname, tbname)
272
+ end
273
+ end
274
+ end
275
+
276
+ class Mysql2MysqlException < Exception
277
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mysql2mysql
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - xianhua.zhou
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-18 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: sequel
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 3
30
+ - 13
31
+ - 0
32
+ version: 3.13.0
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 2
45
+ - 0
46
+ - 1
47
+ version: 2.0.1
48
+ type: :development
49
+ version_requirements: *id002
50
+ description:
51
+ email: xianhua.zhou@gmail.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - Changelog
60
+ - README.rdoc
61
+ - INSTALL
62
+ - lib/mysql2mysql.rb
63
+ - lib/mysql_2_mysql.rb
64
+ has_rdoc: true
65
+ homepage: http://github.com/xianhuazhou
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options: []
70
+
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ requirements: []
90
+
91
+ rubyforge_project: mysql2mysql
92
+ rubygems_version: 1.3.7
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Dump table's structure and data between mysql servers and databases.
96
+ test_files: []
97
+