mysql2mysql 0.0.1

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