mysql2postgres 0.3.3 → 0.4.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +9 -4
- data/Rakefile +1 -1
- data/bin/mysql2postgres +6 -14
- data/lib/mysql2postgres/connection.rb +19 -19
- data/lib/mysql2postgres/converter.rb +0 -1
- data/lib/mysql2postgres/mysql_reader.rb +18 -14
- data/lib/mysql2postgres/postgres_db_writer.rb +4 -5
- data/lib/mysql2postgres/postgres_file_writer.rb +20 -6
- data/lib/mysql2postgres/postgres_writer.rb +65 -30
- data/lib/mysql2postgres/version.rb +1 -1
- data/lib/mysql2postgres.rb +58 -25
- data/mysql2postgres.gemspec +7 -11
- data/test/fixtures/config_all_options.yml +14 -11
- data/test/fixtures/config_min_options.yml +16 -0
- data/test/fixtures/config_to_file.yml +31 -0
- data/test/integration/convert_to_db_test.rb +15 -8
- data/test/integration/convert_to_file_test.rb +33 -18
- data/test/integration/converter_test.rb +10 -3
- data/test/integration/{mysql_reader_base_test.rb → mysql_reader_connection_test.rb} +9 -6
- data/test/integration/mysql_reader_test.rb +7 -4
- data/test/integration/postgres_db_writer_test.rb +24 -0
- data/test/test_helper.rb +22 -67
- data/test/units/mysql_test.rb +10 -0
- data/test/units/option_test.rb +5 -8
- data/test/units/postgres_file_writer_test.rb +4 -2
- metadata +13 -81
- data/lib/mysql2postgres/writer.rb +0 -9
- data/test/integration/postgres_db_writer_base_test.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc905b9634e92298f5b5d192b6f4388294f6153a777b551b408bc5e944247b3f
|
4
|
+
data.tar.gz: 108d90760f0600e55f117df749147f8137d3044a7724dd901f76869db7b45caf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b08edc13623c942b61d03cecce1f577eca9fce3906e3c06ef17c848396dfebadb39017cb7ae7aecb5193e91cffc85d2a4cea2e49054f0e7dd3dffc8f53012eb
|
7
|
+
data.tar.gz: 5ef72f3a6ac5961102d282b38cf2eb5bc62d301f5e84b590dc4e9dd7d057fa51c43f5272b112478ab6b6d3513fc3ac67e8a24bae0d9f23d3f162c0ddd8f591e0
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://github.com/AlphaNodes/mysql2postgres/actions/workflows/rubocop.yml) [](https://github.com/AlphaNodes/mysql2postgres/actions/workflows/tests.yml)
|
4
4
|
|
5
|
-
|
6
|
-
and the next release will have the same requirement.
|
5
|
+
Convert MySQL database to PostgreSQL database.
|
7
6
|
|
8
7
|
## Requirements
|
9
8
|
|
@@ -22,16 +21,22 @@ gem 'mysql2postgres'
|
|
22
21
|
Configuration is written in [YAML format](http://www.yaml.org/ "YAML Ain't Markup Language")
|
23
22
|
and passed as the first argument on the command line.
|
24
23
|
|
25
|
-
Configuration file has be provided with config/database.yml, see config/default.database.yml for
|
26
|
-
an example.
|
24
|
+
Configuration file has be provided with config/database.yml, see [config/default.database.yml](config/default.database.yml) for an example and for configuration information.
|
27
25
|
|
28
26
|
## Usage
|
29
27
|
|
30
28
|
After providing settings, start migration with
|
31
29
|
|
32
30
|
```sh
|
31
|
+
# set destination to use
|
32
|
+
MYSQL2POSTGRES_ENV=test
|
33
|
+
# use can also use (MYSQL2POSTGRES_ENV is used, if both are defined)
|
34
|
+
RAILS_ENV=test
|
33
35
|
|
36
|
+
# with default configuration, which use config/database.yml
|
34
37
|
bundle exec mysql2postgres
|
38
|
+
# OR with specified configuration file
|
39
|
+
bundle exec mysql2postgres /home/you/mysql2postgres.yml
|
35
40
|
```
|
36
41
|
|
37
42
|
## Tests
|
data/Rakefile
CHANGED
data/bin/mysql2postgres
CHANGED
@@ -7,21 +7,13 @@ require 'rubygems'
|
|
7
7
|
require 'bundler/setup'
|
8
8
|
require 'mysql2postgres'
|
9
9
|
|
10
|
-
|
10
|
+
config_file = ARGV.empty? ? File.join(File.dirname(__dir__), 'config', 'database.yml') : File.expand_path(ARGV[0])
|
11
11
|
|
12
|
-
|
13
|
-
ARGV[0]
|
14
|
-
else
|
15
|
-
CONFIG_FILE
|
16
|
-
end
|
12
|
+
raise "'#{config_file}' does not exist" unless FileTest.exist? config_file
|
17
13
|
|
18
|
-
|
19
|
-
raise "'#{file}' does not exist"
|
20
|
-
end
|
14
|
+
db_yaml = YAML.safe_load File.read(config_file)
|
21
15
|
|
22
|
-
|
16
|
+
raise "'#{config_file}' does not contain a mysql configuration directive for conversion" unless db_yaml.key? 'mysql'
|
17
|
+
raise "'#{config_file}' does not contain destinations configuration directive for conversion" unless db_yaml.key? 'destinations'
|
23
18
|
|
24
|
-
|
25
|
-
raise "'#{file}' does not contain a destination configuration directive for conversion" unless db_yaml.key? 'destination'
|
26
|
-
|
27
|
-
Mysql2postgres.new(db_yaml).convert
|
19
|
+
Mysql2postgres.new(db_yaml, config_file).convert
|
@@ -3,29 +3,17 @@
|
|
3
3
|
class Mysql2postgres
|
4
4
|
class Connection
|
5
5
|
attr_reader :conn,
|
6
|
-
:adapter,
|
7
6
|
:hostname,
|
8
7
|
:login,
|
9
8
|
:password,
|
10
9
|
:database,
|
11
10
|
:schema,
|
12
11
|
:port,
|
13
|
-
:environment,
|
14
12
|
:copy_manager,
|
15
13
|
:stream,
|
16
14
|
:is_copying
|
17
15
|
|
18
|
-
def initialize(
|
19
|
-
@environment = (ENV['RAILS_ENV'] || 'development').to_sym
|
20
|
-
|
21
|
-
if options[:destination].nil? ||
|
22
|
-
options[:destination].empty? ||
|
23
|
-
options[:destination][environment].nil? ||
|
24
|
-
options[:destination][environment].empty?
|
25
|
-
raise 'Unable to locate PostgreSQL destination environment in the configuration file'
|
26
|
-
end
|
27
|
-
|
28
|
-
pg_options = options[:destination][environment]
|
16
|
+
def initialize(pg_options)
|
29
17
|
@hostname = pg_options[:hostname] || 'localhost'
|
30
18
|
@login = pg_options[:username]
|
31
19
|
@password = pg_options[:password]
|
@@ -33,18 +21,20 @@ class Mysql2postgres
|
|
33
21
|
@port = (pg_options[:port] || 5432).to_s
|
34
22
|
|
35
23
|
@database, @schema = database.split ':'
|
36
|
-
@adapter = pg_options[:adapter] || 'jdbcpostgresql'
|
37
24
|
|
25
|
+
@conn = open
|
26
|
+
raise_nil_connection if conn.nil?
|
27
|
+
|
28
|
+
@is_copying = false
|
29
|
+
@current_statement = ''
|
30
|
+
end
|
31
|
+
|
32
|
+
def open
|
38
33
|
@conn = PG::Connection.open dbname: database,
|
39
34
|
user: login,
|
40
35
|
password: password,
|
41
36
|
host: hostname,
|
42
37
|
port: port
|
43
|
-
|
44
|
-
raise_nil_connection if conn.nil?
|
45
|
-
|
46
|
-
@is_copying = false
|
47
|
-
@current_statement = ''
|
48
38
|
end
|
49
39
|
|
50
40
|
# ensure that the copy is completed, in case we hadn't seen a '\.' in the data stream.
|
@@ -120,6 +110,16 @@ class Mysql2postgres
|
|
120
110
|
raise 'No Connection'
|
121
111
|
end
|
122
112
|
|
113
|
+
def tables
|
114
|
+
result = run_statement <<~SQL_TABLES
|
115
|
+
SELECT table_name
|
116
|
+
FROM information_schema.tables
|
117
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
118
|
+
SQL_TABLES
|
119
|
+
|
120
|
+
result.map { |t| t['table_name'] }
|
121
|
+
end
|
122
|
+
|
123
123
|
private
|
124
124
|
|
125
125
|
def run_statement(statement)
|
@@ -16,7 +16,6 @@ class Mysql2postgres
|
|
16
16
|
def initialize(reader, writer, options)
|
17
17
|
@reader = reader
|
18
18
|
@writer = writer
|
19
|
-
@options = options
|
20
19
|
@exclude_tables = options[:exclude_tables] || []
|
21
20
|
@only_tables = options[:tables]
|
22
21
|
@suppress_data = options[:suppress_data] || false
|
@@ -164,6 +164,24 @@ class Mysql2postgres
|
|
164
164
|
end
|
165
165
|
end
|
166
166
|
|
167
|
+
attr_reader :mysql
|
168
|
+
|
169
|
+
def initialize(options)
|
170
|
+
@host = options[:mysql][:hostname]
|
171
|
+
@user = options[:mysql][:username]
|
172
|
+
@passwd = options[:mysql][:password]
|
173
|
+
@db = options[:mysql][:database]
|
174
|
+
@port = if options[:mysql][:port]
|
175
|
+
options[:mysql][:port] unless options[:mysql][:port].to_s.empty?
|
176
|
+
else
|
177
|
+
3306
|
178
|
+
end
|
179
|
+
@sock = options[:mysql][:socket] && !options[:mysql][:socket].empty? ? options[:mysql][:socket] : nil
|
180
|
+
@flag = options[:mysql][:flag] && !options[:mysql][:flag].empty? ? options[:mysql][:flag] : nil
|
181
|
+
|
182
|
+
connect
|
183
|
+
end
|
184
|
+
|
167
185
|
def connect
|
168
186
|
@mysql = ::Mysql.connect @host, @user, @passwd, @db, @port, @sock
|
169
187
|
# utf8_unicode_ci :: https://rubydoc.info/gems/ruby-mysql/Mysql/Charset
|
@@ -197,20 +215,6 @@ class Mysql2postgres
|
|
197
215
|
end
|
198
216
|
end
|
199
217
|
|
200
|
-
def initialize(options)
|
201
|
-
@host = options[:mysql][:hostname]
|
202
|
-
@user = options[:mysql][:username]
|
203
|
-
@passwd = options[:mysql][:password]
|
204
|
-
@db = options[:mysql][:database]
|
205
|
-
@port = options[:mysql][:port] || 3306
|
206
|
-
@sock = options[:mysql][:socket] && !options[:mysql][:socket].empty? ? options[:mysql][:socket] : nil
|
207
|
-
@sock = options[:mysql][:flag] && !options[:mysql][:flag].empty? ? options[:mysql][:flag] : nil
|
208
|
-
|
209
|
-
connect
|
210
|
-
end
|
211
|
-
|
212
|
-
attr_reader :mysql
|
213
|
-
|
214
218
|
def tables
|
215
219
|
@tables ||= @mysql.query('SHOW TABLES').map { |row| Table.new(self, row.first) }
|
216
220
|
end
|
@@ -5,14 +5,13 @@ require 'mysql2postgres/connection'
|
|
5
5
|
|
6
6
|
class Mysql2postgres
|
7
7
|
class PostgresDbWriter < PostgresFileWriter
|
8
|
-
attr_reader :connection
|
8
|
+
attr_reader :connection
|
9
9
|
|
10
|
-
def initialize(
|
10
|
+
def initialize(file, destination)
|
11
11
|
# NOTE: the superclass opens and truncates filename for writing
|
12
|
-
super
|
12
|
+
super
|
13
13
|
|
14
|
-
@
|
15
|
-
@connection = Connection.new options
|
14
|
+
@connection = Connection.new destination
|
16
15
|
end
|
17
16
|
|
18
17
|
def inload(path = filename)
|
@@ -1,12 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'mysql2postgres/postgres_writer'
|
4
|
+
require 'fileutils'
|
4
5
|
|
5
6
|
class Mysql2postgres
|
6
7
|
class PostgresFileWriter < PostgresWriter
|
7
|
-
def initialize(file)
|
8
|
+
def initialize(file, destination)
|
8
9
|
super()
|
9
10
|
|
11
|
+
@filename = file
|
12
|
+
@destination = destination
|
13
|
+
|
10
14
|
@f = File.open file, 'w+:UTF-8'
|
11
15
|
@f << <<~SQL_HEADER
|
12
16
|
-- MySQL 2 PostgreSQL dump\n
|
@@ -88,7 +92,7 @@ class Mysql2postgres
|
|
88
92
|
@f << columns
|
89
93
|
|
90
94
|
if (primary_index = table.indexes.find { |index| index[:primary] })
|
91
|
-
@f << ",\n CONSTRAINT #{table.name}_pkey PRIMARY KEY(#{primary_index[:columns]
|
95
|
+
@f << ",\n CONSTRAINT #{table.name}_pkey PRIMARY KEY(#{quoted_list primary_index[:columns]})"
|
92
96
|
end
|
93
97
|
|
94
98
|
@f << <<~SQL_OIDS
|
@@ -103,7 +107,7 @@ class Mysql2postgres
|
|
103
107
|
@f << <<~SQL_INDEX
|
104
108
|
DROP INDEX IF EXISTS #{PG::Connection.quote_ident index[:name]} CASCADE;
|
105
109
|
CREATE #{unique}INDEX #{PG::Connection.quote_ident index[:name]}
|
106
|
-
ON #{PG::Connection.quote_ident table.name} (#{index[:columns]
|
110
|
+
ON #{PG::Connection.quote_ident table.name} (#{quoted_list index[:columns]});
|
107
111
|
SQL_INDEX
|
108
112
|
end
|
109
113
|
end
|
@@ -113,8 +117,8 @@ class Mysql2postgres
|
|
113
117
|
def write_constraints(table)
|
114
118
|
table.foreign_keys.each do |key|
|
115
119
|
@f << "ALTER TABLE #{PG::Connection.quote_ident table.name} " \
|
116
|
-
"ADD FOREIGN KEY (#{key[:column]
|
117
|
-
"REFERENCES #{PG::Connection.quote_ident key[:ref_table]}(#{key[:ref_column]
|
120
|
+
"ADD FOREIGN KEY (#{quoted_list key[:column]}) " \
|
121
|
+
"REFERENCES #{PG::Connection.quote_ident key[:ref_table]}(#{quoted_list key[:ref_column]}) " \
|
118
122
|
"ON UPDATE #{key[:on_update]} ON DELETE #{key[:on_delete]};\n"
|
119
123
|
end
|
120
124
|
end
|
@@ -125,7 +129,7 @@ class Mysql2postgres
|
|
125
129
|
-- Data for Name: #{table.name}; Type: TABLE DATA; Schema: public
|
126
130
|
--
|
127
131
|
|
128
|
-
COPY "#{table.name}" (#{table.columns.map { |
|
132
|
+
COPY "#{table.name}" (#{quoted_list(table.columns.map { |m| m[:name] })}) FROM stdin;
|
129
133
|
SQL_COPY
|
130
134
|
|
131
135
|
reader.paginated_read table, 1000 do |row, _counter|
|
@@ -139,5 +143,15 @@ class Mysql2postgres
|
|
139
143
|
def close
|
140
144
|
@f.close
|
141
145
|
end
|
146
|
+
|
147
|
+
def inload
|
148
|
+
puts "\nSkip import to PostgreSQL DB. SQL file created successfully."
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def quoted_list(list)
|
154
|
+
list.map { |c| PG::Connection.quote_ident(c) }.join(', ')
|
155
|
+
end
|
142
156
|
end
|
143
157
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'zlib'
|
4
|
-
require 'mysql2postgres/writer'
|
5
4
|
|
6
5
|
class Mysql2postgres
|
7
|
-
class PostgresWriter
|
6
|
+
class PostgresWriter
|
7
|
+
attr_reader :filename, :destination
|
8
|
+
|
8
9
|
def column_description(column)
|
9
10
|
"#{PG::Connection.quote_ident column[:name]} #{column_type_info column}"
|
10
11
|
end
|
@@ -57,9 +58,14 @@ class Mysql2postgres
|
|
57
58
|
default = nil
|
58
59
|
'date'
|
59
60
|
when 'timestamp'
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
case column[:default]
|
62
|
+
when 'CURRENT_TIMESTAMP'
|
63
|
+
default = ' DEFAULT CURRENT_TIMESTAMP'
|
64
|
+
when datetime_zero
|
65
|
+
default = " DEFAULT '#{datetime_zero_fix}'"
|
66
|
+
when datetime_zero(with_seconds: true) # rubocop: disable Style/MethodCallWithArgsParentheses
|
67
|
+
default = " DEFAULT '#{datetime_zero_fix with_seconds: true}'"
|
68
|
+
end
|
63
69
|
'timestamp without time zone'
|
64
70
|
when 'time'
|
65
71
|
default = ' DEFAULT NOW()' if default
|
@@ -89,45 +95,74 @@ class Mysql2postgres
|
|
89
95
|
row[index] = Time.at(row[index]).utc.strftime('%H:%M:%S') if column[:type] == 'time' && row[index]
|
90
96
|
|
91
97
|
if row[index].is_a? Time
|
92
|
-
row[index] = row[index].to_s.gsub
|
93
|
-
row[index] = row[index].to_s.gsub
|
98
|
+
row[index] = row[index].to_s.gsub datetime_zero, datetime_zero_fix
|
99
|
+
row[index] = row[index].to_s.gsub datetime_zero(with_seconds: true), datetime_zero_fix(with_seconds: true)
|
94
100
|
end
|
95
101
|
|
96
102
|
if column_type(column) == 'boolean'
|
97
103
|
row[index] = if row[index] == 1
|
98
104
|
't'
|
105
|
+
elsif row[index].zero?
|
106
|
+
'f'
|
99
107
|
else
|
100
|
-
row[index]
|
108
|
+
row[index]
|
101
109
|
end
|
102
110
|
end
|
103
111
|
|
104
|
-
if row[index].is_a? String
|
105
|
-
row[index] = if column_type(column) == 'bytea'
|
106
|
-
if column[:name] == 'data'
|
107
|
-
with_gzip = false
|
108
|
-
table.columns.each_with_index do |column_data, index_data|
|
109
|
-
if column_data[:name] == 'compression' && row[index_data] == 'gzip'
|
110
|
-
with_gzip = true
|
111
|
-
break
|
112
|
-
end
|
113
|
-
end
|
114
|
-
if with_gzip
|
115
|
-
PG::Connection.escape_bytea(Zlib::Inflate.inflate(row[index])).gsub(/\\/, '\\\\\\').gsub(/''/, "'")
|
116
|
-
else
|
117
|
-
PG::Connection.escape_bytea(row[index]).gsub(/\\/, '\\\\\\').gsub(/''/, "'")
|
118
|
-
end
|
119
|
-
else
|
120
|
-
PG::Connection.escape_bytea(row[index]).gsub(/\\/, '\\\\\\').gsub(/''/, "'")
|
121
|
-
end
|
122
|
-
else
|
123
|
-
row[index].gsub(/\\/, '\\\\\\').gsub(/\n/, '\n').gsub(/\t/, '\t').gsub(/\r/, '\r').gsub(/\0/, '')
|
124
|
-
end
|
125
|
-
end
|
112
|
+
row[index] = string_data row, index, column if row[index].is_a? String
|
126
113
|
|
127
114
|
row[index] = '\N' unless row[index]
|
128
115
|
end
|
129
116
|
end
|
130
117
|
|
131
118
|
def truncate(_table) end
|
119
|
+
|
120
|
+
def inload
|
121
|
+
raise "Method 'inload' needs to be overridden..."
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def datetime_zero(with_seconds: false)
|
127
|
+
datetime_value date: '0000-00-00', with_seconds: with_seconds
|
128
|
+
end
|
129
|
+
|
130
|
+
def datetime_zero_fix(with_seconds: false)
|
131
|
+
datetime_value date: '1970-01-01', with_seconds: with_seconds
|
132
|
+
end
|
133
|
+
|
134
|
+
def datetime_value(date:, with_seconds: false)
|
135
|
+
value = ["#{date} 00:00"]
|
136
|
+
value << '00' if with_seconds
|
137
|
+
value.join ':'
|
138
|
+
end
|
139
|
+
|
140
|
+
def string_data(row, index, column)
|
141
|
+
if column_type(column) == 'bytea'
|
142
|
+
if column[:name] == 'data'
|
143
|
+
with_gzip = false
|
144
|
+
table.columns.each_with_index do |column_data, index_data|
|
145
|
+
if column_data[:name] == 'compression' && row[index_data] == 'gzip'
|
146
|
+
with_gzip = true
|
147
|
+
break
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
escape_bytea(with_gzip ? Zlib::Inflate.inflate(row[index]) : row[index])
|
152
|
+
else
|
153
|
+
escape_bytea row[index]
|
154
|
+
end
|
155
|
+
else
|
156
|
+
escape_data(row[index]).gsub(/\n/, '\n').gsub(/\t/, '\t').gsub(/\r/, '\r').gsub(/\0/, '')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def escape_bytea(data)
|
161
|
+
escape_data(PG::Connection.escape_bytea(data)).gsub(/''/, "'")
|
162
|
+
end
|
163
|
+
|
164
|
+
def escape_data(value)
|
165
|
+
value.gsub(/\\/, '\\\\\\')
|
166
|
+
end
|
132
167
|
end
|
133
168
|
end
|
data/lib/mysql2postgres.rb
CHANGED
@@ -12,7 +12,6 @@ require 'pg/result'
|
|
12
12
|
require 'mysql2postgres/version'
|
13
13
|
require 'mysql2postgres/converter'
|
14
14
|
require 'mysql2postgres/mysql_reader'
|
15
|
-
require 'mysql2postgres/writer'
|
16
15
|
require 'mysql2postgres/postgres_writer'
|
17
16
|
require 'mysql2postgres/postgres_file_writer'
|
18
17
|
require 'mysql2postgres/postgres_db_writer'
|
@@ -20,42 +19,76 @@ require 'mysql2postgres/postgres_db_writer'
|
|
20
19
|
require 'debug' if ENV.fetch('ENABLE_DEBUG', nil) == '1'
|
21
20
|
|
22
21
|
class Mysql2postgres
|
23
|
-
attr_reader :options, :reader, :writer
|
22
|
+
attr_reader :options, :config_file, :reader, :writer
|
24
23
|
|
25
|
-
def initialize(yaml)
|
24
|
+
def initialize(yaml, config_file = nil)
|
25
|
+
@config_file = config_file
|
26
26
|
@options = build_options yaml
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
29
|
+
def convert
|
30
|
+
@reader = MysqlReader.new options
|
31
|
+
|
32
|
+
puts "mysql2postgres #{Mysql2postgres::VERSION}"
|
33
|
+
puts "Config file: #{config_file}"
|
34
|
+
puts "Dumpfile: #{dump_file}"
|
35
|
+
|
36
|
+
@writer = if to_file?
|
37
|
+
puts 'Target: File'
|
38
|
+
PostgresFileWriter.new dump_file, options[:destination]
|
39
|
+
else
|
40
|
+
puts "Target: PostgreSQL DB (#{adapter})"
|
41
|
+
PostgresDbWriter.new dump_file, options[:destination]
|
42
|
+
end
|
38
43
|
|
39
|
-
|
40
|
-
|
41
|
-
connection.load_file path
|
44
|
+
Converter.new(reader, writer, options).convert
|
45
|
+
File.delete dump_file if options[:remove_dump_file] && File.exist?(dump_file)
|
42
46
|
end
|
43
47
|
|
44
|
-
|
45
|
-
@reader = MysqlReader.new options
|
48
|
+
private
|
46
49
|
|
47
|
-
|
50
|
+
def adapter
|
51
|
+
if options[:destination][:adapter].nil? || options[:destination][:adapter].empty?
|
52
|
+
'postgresql'
|
53
|
+
else
|
54
|
+
options[:destination][:adapter]
|
55
|
+
end
|
56
|
+
end
|
48
57
|
|
49
|
-
|
50
|
-
|
58
|
+
def environment
|
59
|
+
if ENV['MYSQL2POSTGRES_ENV']
|
60
|
+
ENV['MYSQL2POSTGRES_ENV']
|
61
|
+
elsif ENV['RAILS_ENV']
|
62
|
+
ENV['RAILS_ENV']
|
63
|
+
else
|
64
|
+
'development'
|
65
|
+
end
|
66
|
+
end
|
51
67
|
|
52
|
-
|
53
|
-
|
68
|
+
def to_file?
|
69
|
+
adapter == 'file'
|
70
|
+
end
|
54
71
|
|
55
|
-
|
72
|
+
def build_options(yaml)
|
73
|
+
yaml.transform_keys(&:to_sym).tap do |opts|
|
74
|
+
opts[:mysql].transform_keys!(&:to_sym)
|
56
75
|
|
57
|
-
|
76
|
+
destinations = opts.delete :destinations
|
77
|
+
opts[:destination] = destinations[environment]&.transform_keys(&:to_sym)
|
78
|
+
|
79
|
+
if opts[:destination].nil? || opts[:destination].empty?
|
80
|
+
raise "no configuration for environment '#{environment}' in destinations available. Use MYSQL2POSTGRES_ENV or RAILS_ENV."
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
58
84
|
|
59
|
-
|
85
|
+
def dump_file
|
86
|
+
@dump_file ||= if to_file? && options[:destination][:filename] && options[:destination][:filename] != ''
|
87
|
+
options[:destination][:filename]
|
88
|
+
else
|
89
|
+
tag = Time.new.strftime '%Y%m%d-%H%M%S'
|
90
|
+
path = options[:dump_file_directory] || './'
|
91
|
+
File.expand_path File.join(path, "output_#{tag}.sql")
|
92
|
+
end
|
60
93
|
end
|
61
94
|
end
|
data/mysql2postgres.gemspec
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
lib = File.expand_path '../lib', __FILE__
|
4
|
-
puts lib
|
5
4
|
$LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib
|
6
5
|
require 'mysql2postgres/version'
|
7
6
|
|
@@ -50,31 +49,28 @@ Gem::Specification.new do |s|
|
|
50
49
|
'lib/mysql2postgres/postgres_db_writer.rb',
|
51
50
|
'lib/mysql2postgres/postgres_writer.rb',
|
52
51
|
'lib/mysql2postgres/version.rb',
|
53
|
-
'lib/mysql2postgres/writer.rb',
|
54
52
|
'mysql2postgres.gemspec',
|
55
53
|
'test/fixtures/config_all_options.yml',
|
54
|
+
'test/fixtures/config_min_options.yml',
|
55
|
+
'test/fixtures/config_to_file.yml',
|
56
56
|
'test/fixtures/seed_integration_tests.sql',
|
57
57
|
'test/integration/convert_to_db_test.rb',
|
58
58
|
'test/integration/convert_to_file_test.rb',
|
59
59
|
'test/integration/converter_test.rb',
|
60
|
-
'test/integration/
|
60
|
+
'test/integration/mysql_reader_connection_test.rb',
|
61
61
|
'test/integration/mysql_reader_test.rb',
|
62
|
-
'test/integration/
|
62
|
+
'test/integration/postgres_db_writer_test.rb',
|
63
|
+
'test/units/mysql_test.rb',
|
63
64
|
'test/units/option_test.rb',
|
64
65
|
'test/units/postgres_file_writer_test.rb',
|
65
66
|
'test/test_helper.rb'
|
66
67
|
]
|
67
|
-
s.homepage = 'https://
|
68
|
+
s.homepage = 'https://github.com/AlphaNodes/mysql2postgres'
|
68
69
|
s.rdoc_options = ['--charset=UTF-8']
|
69
70
|
s.require_paths = ['lib']
|
70
71
|
s.summary = 'MySQL to PostgreSQL Data Translation'
|
71
72
|
|
72
|
-
s.add_dependency 'rake'
|
73
73
|
s.add_runtime_dependency 'pg', '~> 1.2.2'
|
74
|
-
s.add_runtime_dependency '
|
74
|
+
s.add_runtime_dependency 'rake'
|
75
75
|
s.add_runtime_dependency 'ruby-mysql', '~> 3.0'
|
76
|
-
s.add_development_dependency 'debug'
|
77
|
-
s.add_development_dependency 'rubocop-minitest'
|
78
|
-
s.add_development_dependency 'rubocop-performance'
|
79
|
-
s.add_development_dependency 'test-unit', '~> 3.5.3'
|
80
76
|
end
|
@@ -1,20 +1,21 @@
|
|
1
1
|
mysql:
|
2
|
-
hostname:
|
2
|
+
hostname: 127.0.0.1
|
3
3
|
port: 3306
|
4
|
-
|
5
|
-
|
6
|
-
password: secretpassword
|
4
|
+
username: root
|
5
|
+
password: BestPasswordEver
|
7
6
|
database: somename
|
7
|
+
encoding: utf8mb4
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
# if MYSQL2POSTGRES_ENV and RAILS_ENV is missing, development is used!
|
10
|
+
destinations:
|
11
|
+
development:
|
12
|
+
adapter: file
|
13
|
+
filename: /tmp/exported_file.sql
|
13
14
|
test:
|
14
15
|
hostname: localhost
|
15
16
|
port: 5432
|
16
|
-
username:
|
17
|
-
password:
|
17
|
+
username: postgres
|
18
|
+
password: postgres
|
18
19
|
database: somename
|
19
20
|
|
20
21
|
# if tables is given, only the listed tables will be converted. leave empty to convert all tables.
|
@@ -29,8 +30,10 @@ exclude_tables:
|
|
29
30
|
- table5
|
30
31
|
- table6
|
31
32
|
|
33
|
+
dump_file_directory: /tmp
|
34
|
+
|
32
35
|
# if suppress_data is true, only the schema definition will be exported/migrated, and not the data
|
33
|
-
suppress_data:
|
36
|
+
suppress_data: false
|
34
37
|
|
35
38
|
# if suppress_ddl is true, only the data will be exported/imported, and not the schema
|
36
39
|
suppress_ddl: false
|
@@ -0,0 +1,16 @@
|
|
1
|
+
mysql:
|
2
|
+
hostname: localhost
|
3
|
+
username: root
|
4
|
+
password: BestPasswordEver
|
5
|
+
database: somename
|
6
|
+
|
7
|
+
# if MYSQL2POSTGRES_ENV and RAILS_ENV is missing, development is used!
|
8
|
+
destinations:
|
9
|
+
test:
|
10
|
+
hostname: localhost
|
11
|
+
port: 5432
|
12
|
+
username: somename
|
13
|
+
password: secretpassword
|
14
|
+
database: somename
|
15
|
+
|
16
|
+
# all options are false as default
|
@@ -0,0 +1,31 @@
|
|
1
|
+
mysql:
|
2
|
+
hostname: 127.0.0.1
|
3
|
+
port: 3306
|
4
|
+
username: root
|
5
|
+
password: BestPasswordEver
|
6
|
+
database: somename
|
7
|
+
encoding: utf8mb4
|
8
|
+
|
9
|
+
# if MYSQL2POSTGRES_ENV and RAILS_ENV is missing, development is used!
|
10
|
+
destinations:
|
11
|
+
test:
|
12
|
+
adapter: file
|
13
|
+
filename: /tmp/exported_file.sql
|
14
|
+
|
15
|
+
# if tables is given, only the listed tables will be converted. leave empty to convert all tables.
|
16
|
+
tables:
|
17
|
+
- table1
|
18
|
+
- table2
|
19
|
+
- table3
|
20
|
+
- table4
|
21
|
+
|
22
|
+
dump_file_directory: /tmp
|
23
|
+
|
24
|
+
# if suppress_data is true, only the schema definition will be exported/migrated, and not the data
|
25
|
+
suppress_data: false
|
26
|
+
|
27
|
+
# if suppress_ddl is true, only the data will be exported/imported, and not the schema
|
28
|
+
suppress_ddl: false
|
29
|
+
|
30
|
+
# if force_truncate is true, forces a table truncate before table loading
|
31
|
+
force_truncate: false
|
@@ -6,18 +6,25 @@ class ConvertToDbTest < Test::Unit::TestCase
|
|
6
6
|
class << self
|
7
7
|
def startup
|
8
8
|
seed_test_database
|
9
|
-
@options = get_test_config_by_label :localmysql_to_db_convert_all
|
10
|
-
@mysql2postgres = Mysql2postgres.new @options
|
11
|
-
@mysql2postgres.convert
|
12
|
-
@mysql2postgres.writer.open
|
13
9
|
end
|
10
|
+
end
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
def setup
|
13
|
+
@mysql2postgres = instance_from_file
|
14
|
+
@options = @mysql2postgres.options
|
15
|
+
@options[:force_truncate] = true
|
16
|
+
@options.delete :tables # convert all available tables
|
17
|
+
|
18
|
+
@mysql2postgres.convert
|
19
|
+
@mysql2postgres.writer.connection.open
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
@mysql2postgres&.writer&.connection&.finish
|
18
24
|
end
|
19
25
|
|
20
26
|
def test_table_creation
|
21
|
-
|
27
|
+
tables = @mysql2postgres.writer.connection.tables
|
28
|
+
assert tables.include?('numeric_types_basics')
|
22
29
|
end
|
23
30
|
end
|
@@ -3,18 +3,22 @@
|
|
3
3
|
require File.expand_path '../test_helper', __dir__
|
4
4
|
|
5
5
|
class ConvertToFileTest < Test::Unit::TestCase
|
6
|
+
attr_reader :content
|
7
|
+
|
6
8
|
class << self
|
7
9
|
def startup
|
8
|
-
seed_test_database
|
9
|
-
@options = get_test_config_by_label :localmysql_to_file_convert_all
|
10
|
-
@mysql2postgres = Mysql2postgres.new @options
|
11
|
-
@mysql2postgres.convert
|
12
|
-
@content = File.read @mysql2postgres.options[:destfile]
|
10
|
+
seed_test_database option_file: 'config_to_file'
|
13
11
|
end
|
14
12
|
end
|
15
13
|
|
16
|
-
def
|
17
|
-
|
14
|
+
def setup
|
15
|
+
@mysql2postgres = instance_from_file 'config_to_file'
|
16
|
+
@options = @mysql2postgres.options
|
17
|
+
@options[:force_truncate] = true
|
18
|
+
@options.delete :tables # convert all available tables
|
19
|
+
|
20
|
+
@mysql2postgres.convert
|
21
|
+
@content = File.read @mysql2postgres.options[:destination][:filename]
|
18
22
|
end
|
19
23
|
|
20
24
|
def test_table_creation
|
@@ -23,46 +27,57 @@ class ConvertToFileTest < Test::Unit::TestCase
|
|
23
27
|
end
|
24
28
|
|
25
29
|
def test_basic_numerics_tinyint
|
26
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_tinyint" smallint,.*\)', Regexp::MULTILINE)
|
30
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_tinyint" smallint,.*\)', Regexp::MULTILINE)
|
31
|
+
.match(content)
|
27
32
|
end
|
28
33
|
|
29
34
|
def test_basic_numerics_smallint
|
30
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_smallint" integer,.*\)', Regexp::MULTILINE)
|
35
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_smallint" integer,.*\)', Regexp::MULTILINE)
|
36
|
+
.match(content)
|
31
37
|
end
|
32
38
|
|
33
39
|
def test_basic_numerics_mediumint
|
34
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_mediumint" integer,.*\)', Regexp::MULTILINE)
|
40
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_mediumint" integer,.*\)', Regexp::MULTILINE)
|
41
|
+
.match(content)
|
35
42
|
end
|
36
43
|
|
37
44
|
def test_basic_numerics_int
|
38
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_int" integer,.*\)', Regexp::MULTILINE)
|
45
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_int" integer,.*\)', Regexp::MULTILINE)
|
46
|
+
.match(content)
|
39
47
|
end
|
40
48
|
|
41
49
|
def test_basic_numerics_integer
|
42
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_integer" integer,.*\)', Regexp::MULTILINE)
|
50
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_integer" integer,.*\)', Regexp::MULTILINE)
|
51
|
+
.match(content)
|
43
52
|
end
|
44
53
|
|
45
54
|
def test_basic_numerics_bigint
|
46
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_bigint" bigint,.*\)', Regexp::MULTILINE)
|
55
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_bigint" bigint,.*\)', Regexp::MULTILINE)
|
56
|
+
.match(content)
|
47
57
|
end
|
48
58
|
|
49
59
|
def test_basic_numerics_real
|
50
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_real" double precision,.*\)', Regexp::MULTILINE)
|
60
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_real" double precision,.*\)', Regexp::MULTILINE)
|
61
|
+
.match(content)
|
51
62
|
end
|
52
63
|
|
53
64
|
def test_basic_numerics_double
|
54
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_double" double precision,.*\)', Regexp::MULTILINE)
|
65
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_double" double precision,.*\)', Regexp::MULTILINE)
|
66
|
+
.match(content)
|
55
67
|
end
|
56
68
|
|
57
69
|
def test_basic_numerics_float
|
58
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_float" double precision,.*\)', Regexp::MULTILINE)
|
70
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_float" double precision,.*\)', Regexp::MULTILINE)
|
71
|
+
.match(content)
|
59
72
|
end
|
60
73
|
|
61
74
|
def test_basic_numerics_decimal
|
62
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_decimal" numeric\(10, 0\),.*\)', Regexp::MULTILINE)
|
75
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_decimal" numeric\(10, 0\),.*\)', Regexp::MULTILINE)
|
76
|
+
.match(content)
|
63
77
|
end
|
64
78
|
|
65
79
|
def test_basic_numerics_numeric
|
66
|
-
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_numeric" numeric\(10, 0\)[\w\n]*\)', Regexp::MULTILINE)
|
80
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_numeric" numeric\(10, 0\)[\w\n]*\)', Regexp::MULTILINE)
|
81
|
+
.match(content)
|
67
82
|
end
|
68
83
|
end
|
@@ -5,15 +5,22 @@ require File.expand_path '../test_helper', __dir__
|
|
5
5
|
class ConverterTest < Test::Unit::TestCase
|
6
6
|
class << self
|
7
7
|
def startup
|
8
|
-
seed_test_database
|
9
|
-
@options = get_test_config_by_label :localmysql_to_file_convert_nothing
|
8
|
+
seed_test_database option_file: 'config_to_file'
|
10
9
|
end
|
11
10
|
end
|
12
11
|
|
12
|
+
def setup
|
13
|
+
@options = options_from_file 'config_to_file'
|
14
|
+
@options[:suppress_data] = true
|
15
|
+
@options[:suppress_ddl] = true
|
16
|
+
|
17
|
+
@destfile = get_temp_file 'mysql2postgres_test'
|
18
|
+
end
|
19
|
+
|
13
20
|
def test_new_converter
|
14
21
|
assert_nothing_raised do
|
15
22
|
reader = get_test_reader @options
|
16
|
-
writer =
|
23
|
+
writer = Mysql2postgres::PostgresFileWriter.new @destfile, @options[:destination]
|
17
24
|
converter = Mysql2postgres::Converter.new reader, writer, @options
|
18
25
|
assert_equal 0, converter.convert
|
19
26
|
end
|
@@ -2,23 +2,26 @@
|
|
2
2
|
|
3
3
|
require File.expand_path '../test_helper', __dir__
|
4
4
|
|
5
|
-
class
|
5
|
+
class MysqlReaderConnectionTest < Test::Unit::TestCase
|
6
6
|
class << self
|
7
7
|
def startup
|
8
|
-
seed_test_database
|
9
|
-
@options = get_test_config_by_label :localmysql_to_file_convert_nothing
|
8
|
+
seed_test_database option_file: 'config_to_file'
|
10
9
|
end
|
11
10
|
end
|
12
11
|
|
12
|
+
def setup
|
13
|
+
@options = options_from_file 'config_to_file'
|
14
|
+
end
|
15
|
+
|
13
16
|
def test_mysql_connection
|
14
17
|
assert_nothing_raised do
|
15
|
-
|
18
|
+
Mysql2postgres::MysqlReader.new @options
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
19
22
|
def test_mysql_reconnect
|
20
23
|
assert_nothing_raised do
|
21
|
-
reader =
|
24
|
+
reader = Mysql2postgres::MysqlReader.new @options
|
22
25
|
reader.reconnect
|
23
26
|
end
|
24
27
|
end
|
@@ -27,7 +30,7 @@ class MysqlReaderBaseTest < Test::Unit::TestCase
|
|
27
30
|
assert_nothing_raised do
|
28
31
|
@options[:mysql][:port] = ''
|
29
32
|
@options[:mysql][:socket] = ''
|
30
|
-
|
33
|
+
Mysql2postgres::MysqlReader.new @options
|
31
34
|
end
|
32
35
|
end
|
33
36
|
end
|
@@ -5,12 +5,15 @@ require File.expand_path '../test_helper', __dir__
|
|
5
5
|
class MysqlReaderTest < Test::Unit::TestCase
|
6
6
|
class << self
|
7
7
|
def startup
|
8
|
-
seed_test_database
|
9
|
-
@options = get_test_config_by_label :localmysql_to_file_convert_nothing
|
10
|
-
@reader = get_test_reader @options
|
8
|
+
seed_test_database option_file: 'config_to_file'
|
11
9
|
end
|
12
10
|
end
|
13
11
|
|
12
|
+
def setup
|
13
|
+
@options = options_from_file 'config_to_file'
|
14
|
+
@reader = get_test_reader @options
|
15
|
+
end
|
16
|
+
|
14
17
|
def test_db_connection
|
15
18
|
assert_nothing_raised do
|
16
19
|
@reader.mysql.ping
|
@@ -26,7 +29,7 @@ class MysqlReaderTest < Test::Unit::TestCase
|
|
26
29
|
def test_paginated_read
|
27
30
|
expected_rows = 3
|
28
31
|
page_size = 2
|
29
|
-
expected_pages = (1.0 * expected_rows / page_size).ceil
|
32
|
+
# expected_pages = (1.0 * expected_rows / page_size).ceil
|
30
33
|
|
31
34
|
row_count = my_row_count = 0
|
32
35
|
table = @reader.tables.find { |t| t.name == 'numeric_types_basics' }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path '../test_helper', __dir__
|
4
|
+
|
5
|
+
class PostgresDbWriterTest < Test::Unit::TestCase
|
6
|
+
class << self
|
7
|
+
def startup
|
8
|
+
seed_test_database
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@options = options_from_file
|
14
|
+
@options[:suppress_data] = true
|
15
|
+
@options[:suppress_ddl] = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_pg_connection
|
19
|
+
assert_nothing_raised do
|
20
|
+
Mysql2postgres::PostgresDbWriter.new Tempfile.new('mysql2postgres_test_').path,
|
21
|
+
@options[:destination]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -4,47 +4,37 @@ require 'rubygems'
|
|
4
4
|
require 'test/unit'
|
5
5
|
require 'debug' if ENV.fetch('ENABLE_DEBUG', nil) == '1'
|
6
6
|
|
7
|
-
require File.expand_path('
|
8
|
-
require File.expand_path('../lib/mysql2postgres/converter', __dir__)
|
9
|
-
require File.expand_path('../lib/mysql2postgres/mysql_reader', __dir__)
|
10
|
-
require File.expand_path('../lib/mysql2postgres/writer', __dir__)
|
11
|
-
require File.expand_path('../lib/mysql2postgres/postgres_writer', __dir__)
|
12
|
-
require File.expand_path('../lib/mysql2postgres/postgres_file_writer', __dir__)
|
13
|
-
require File.expand_path('../lib/mysql2postgres/postgres_db_writer', __dir__)
|
7
|
+
require File.expand_path('lib/mysql2postgres')
|
14
8
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
rc = system "mysql -u#{options[:mysql][:username]} #{options[:mysql][:database]} < #{seedfilepath}"
|
19
|
-
raise StandardError unless rc
|
9
|
+
def load_yaml_file(file = 'config_all_options')
|
10
|
+
YAML.load_file "#{__dir__}/fixtures/#{file}.yml"
|
11
|
+
end
|
20
12
|
|
21
|
-
|
22
|
-
|
23
|
-
raise 'Failed to seed integration test db. See README for setup requirements.'
|
13
|
+
def instance_from_file(file = 'config_all_options')
|
14
|
+
Mysql2postgres.new load_yaml_file(file)
|
24
15
|
end
|
25
16
|
|
26
|
-
def
|
27
|
-
|
28
|
-
mysql2postgres::MysqlReader.new options
|
29
|
-
rescue StandardError
|
30
|
-
raise 'Failed to initialize integration test db. See README for setup requirements.'
|
17
|
+
def options_from_file(file = 'config_all_options')
|
18
|
+
instance_from_file(file).options
|
31
19
|
end
|
32
20
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
21
|
+
def seed_test_database(option_file: 'config_all_options', sql_file: 'seed_integration_tests.sql')
|
22
|
+
options = options_from_file option_file
|
23
|
+
seedfilepath = File.expand_path "test/fixtures/#{sql_file}"
|
24
|
+
system 'mysql ' \
|
25
|
+
"--host #{options[:mysql][:hostname]} " \
|
26
|
+
"--port #{options[:mysql][:port]} " \
|
27
|
+
"-u#{options[:mysql][:username]} " \
|
28
|
+
"-p#{options[:mysql][:password]} " \
|
29
|
+
"#{options[:mysql][:database]} < #{seedfilepath}", exception: true
|
30
|
+
rescue StandardError
|
31
|
+
raise 'Failed to seed integration test db. See README for setup requirements.'
|
39
32
|
end
|
40
33
|
|
41
|
-
def
|
42
|
-
|
43
|
-
reader = get_test_reader options
|
44
|
-
writer = get_test_file_writer options
|
45
|
-
mysql2postgres::Converter.new reader, writer, options
|
34
|
+
def get_test_reader(options)
|
35
|
+
Mysql2postgres::MysqlReader.new options
|
46
36
|
rescue StandardError
|
47
|
-
raise
|
37
|
+
raise 'Failed to initialize integration test db. See README for setup requirements.'
|
48
38
|
end
|
49
39
|
|
50
40
|
def get_temp_file(basename)
|
@@ -54,38 +44,3 @@ def get_temp_file(basename)
|
|
54
44
|
f.close!
|
55
45
|
path
|
56
46
|
end
|
57
|
-
|
58
|
-
def get_new_test_config(include_tables: [], exclude_tables: [], to_file: false,
|
59
|
-
suppress_data: false, suppress_ddl: false, force_truncate: false)
|
60
|
-
|
61
|
-
to_filename = to_file ? get_temp_file(['mysql2postgres_tmp_output_', '.yml']) : nil
|
62
|
-
configtext = Mysql2postgres::Config.template to_filename, include_tables, exclude_tables, suppress_data, suppress_ddl, force_truncate
|
63
|
-
configfile = get_temp_file 'mysql2postgres_tmp_config'
|
64
|
-
File.open(configfile, 'w:UTF-8') { |f| f.write(configtext) }
|
65
|
-
yaml = YAML.load_file configfile
|
66
|
-
Mysql2postgres::ConfigBase.new yaml
|
67
|
-
rescue StandardError
|
68
|
-
raise "Failed to initialize options from #{configfile}. See README for setup requirements."
|
69
|
-
end
|
70
|
-
|
71
|
-
def get_test_config_by_label(name)
|
72
|
-
case name
|
73
|
-
when :localmysql_to_file_convert_nothing
|
74
|
-
get_new_test_config to_file: true,
|
75
|
-
tables: ['unobtainium'],
|
76
|
-
exclude_tables: ['kryptonite'],
|
77
|
-
suppress_data: true,
|
78
|
-
suppress_ddl: true
|
79
|
-
when :localmysql_to_file_convert_all
|
80
|
-
get_new_test_config to_file: true
|
81
|
-
when :localmysql_to_db_convert_all
|
82
|
-
get_new_test_config
|
83
|
-
when :localmysql_to_db_convert_nothing
|
84
|
-
get_new_test_config tables: ['unobtainium'],
|
85
|
-
exclude_tables: ['kryptonite'],
|
86
|
-
suppress_data: true,
|
87
|
-
suppress_ddl: true
|
88
|
-
else
|
89
|
-
raise "Invalid label: #{name}"
|
90
|
-
end
|
91
|
-
end
|
data/test/units/option_test.rb
CHANGED
@@ -4,14 +4,11 @@ require File.expand_path '../test_helper', __dir__
|
|
4
4
|
require 'yaml'
|
5
5
|
|
6
6
|
class SettingTest < Test::Unit::TestCase
|
7
|
-
attr_reader :config_all_opts
|
8
|
-
|
9
|
-
def setup
|
10
|
-
@config_all_opts = YAML.load_file "#{__dir__}/../fixtures/config_all_options.yml"
|
11
|
-
end
|
12
|
-
|
13
7
|
def test_options_loaded
|
14
|
-
|
15
|
-
|
8
|
+
options = options_from_file
|
9
|
+
|
10
|
+
assert_equal false, options[:suppress_data]
|
11
|
+
assert_equal 'postgres', options[:destination][:username]
|
12
|
+
assert_equal 'somename', options[:destination][:database]
|
16
13
|
end
|
17
14
|
end
|
@@ -15,10 +15,12 @@ class PostgresFileWriterTest < Test::Unit::TestCase
|
|
15
15
|
File.delete destfile
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
|
18
|
+
def test_file_writer
|
19
|
+
destination = { filename: '/tmp/test.sql' }
|
20
|
+
writer = Mysql2postgres::PostgresFileWriter.new @destfile, destination
|
20
21
|
writer.close
|
21
22
|
content = File.read destfile
|
23
|
+
|
22
24
|
assert_not_nil content.match("SET client_encoding = 'UTF8'")
|
23
25
|
assert_nil content.match('unobtanium')
|
24
26
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql2postgres
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Max Lapshin <max@maxidoors.ru>
|
@@ -24,22 +24,8 @@ authors:
|
|
24
24
|
autorequire:
|
25
25
|
bindir: bin
|
26
26
|
cert_chain: []
|
27
|
-
date: 2022-07-
|
27
|
+
date: 2022-07-31 00:00:00.000000000 Z
|
28
28
|
dependencies:
|
29
|
-
- !ruby/object:Gem::Dependency
|
30
|
-
name: rake
|
31
|
-
requirement: !ruby/object:Gem::Requirement
|
32
|
-
requirements:
|
33
|
-
- - ">="
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version: '0'
|
36
|
-
type: :runtime
|
37
|
-
prerelease: false
|
38
|
-
version_requirements: !ruby/object:Gem::Requirement
|
39
|
-
requirements:
|
40
|
-
- - ">="
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: '0'
|
43
29
|
- !ruby/object:Gem::Dependency
|
44
30
|
name: pg
|
45
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,19 +41,19 @@ dependencies:
|
|
55
41
|
- !ruby/object:Gem::Version
|
56
42
|
version: 1.2.2
|
57
43
|
- !ruby/object:Gem::Dependency
|
58
|
-
name:
|
44
|
+
name: rake
|
59
45
|
requirement: !ruby/object:Gem::Requirement
|
60
46
|
requirements:
|
61
|
-
- - "
|
47
|
+
- - ">="
|
62
48
|
- !ruby/object:Gem::Version
|
63
|
-
version: '0
|
49
|
+
version: '0'
|
64
50
|
type: :runtime
|
65
51
|
prerelease: false
|
66
52
|
version_requirements: !ruby/object:Gem::Requirement
|
67
53
|
requirements:
|
68
|
-
- - "
|
54
|
+
- - ">="
|
69
55
|
- !ruby/object:Gem::Version
|
70
|
-
version: '0
|
56
|
+
version: '0'
|
71
57
|
- !ruby/object:Gem::Dependency
|
72
58
|
name: ruby-mysql
|
73
59
|
requirement: !ruby/object:Gem::Requirement
|
@@ -82,62 +68,6 @@ dependencies:
|
|
82
68
|
- - "~>"
|
83
69
|
- !ruby/object:Gem::Version
|
84
70
|
version: '3.0'
|
85
|
-
- !ruby/object:Gem::Dependency
|
86
|
-
name: debug
|
87
|
-
requirement: !ruby/object:Gem::Requirement
|
88
|
-
requirements:
|
89
|
-
- - ">="
|
90
|
-
- !ruby/object:Gem::Version
|
91
|
-
version: '0'
|
92
|
-
type: :development
|
93
|
-
prerelease: false
|
94
|
-
version_requirements: !ruby/object:Gem::Requirement
|
95
|
-
requirements:
|
96
|
-
- - ">="
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
version: '0'
|
99
|
-
- !ruby/object:Gem::Dependency
|
100
|
-
name: rubocop-minitest
|
101
|
-
requirement: !ruby/object:Gem::Requirement
|
102
|
-
requirements:
|
103
|
-
- - ">="
|
104
|
-
- !ruby/object:Gem::Version
|
105
|
-
version: '0'
|
106
|
-
type: :development
|
107
|
-
prerelease: false
|
108
|
-
version_requirements: !ruby/object:Gem::Requirement
|
109
|
-
requirements:
|
110
|
-
- - ">="
|
111
|
-
- !ruby/object:Gem::Version
|
112
|
-
version: '0'
|
113
|
-
- !ruby/object:Gem::Dependency
|
114
|
-
name: rubocop-performance
|
115
|
-
requirement: !ruby/object:Gem::Requirement
|
116
|
-
requirements:
|
117
|
-
- - ">="
|
118
|
-
- !ruby/object:Gem::Version
|
119
|
-
version: '0'
|
120
|
-
type: :development
|
121
|
-
prerelease: false
|
122
|
-
version_requirements: !ruby/object:Gem::Requirement
|
123
|
-
requirements:
|
124
|
-
- - ">="
|
125
|
-
- !ruby/object:Gem::Version
|
126
|
-
version: '0'
|
127
|
-
- !ruby/object:Gem::Dependency
|
128
|
-
name: test-unit
|
129
|
-
requirement: !ruby/object:Gem::Requirement
|
130
|
-
requirements:
|
131
|
-
- - "~>"
|
132
|
-
- !ruby/object:Gem::Version
|
133
|
-
version: 3.5.3
|
134
|
-
type: :development
|
135
|
-
prerelease: false
|
136
|
-
version_requirements: !ruby/object:Gem::Requirement
|
137
|
-
requirements:
|
138
|
-
- - "~>"
|
139
|
-
- !ruby/object:Gem::Version
|
140
|
-
version: 3.5.3
|
141
71
|
description: Translates MySQL -> PostgreSQL
|
142
72
|
email: a.meindl@alphanodes.com
|
143
73
|
executables:
|
@@ -158,20 +88,22 @@ files:
|
|
158
88
|
- lib/mysql2postgres/postgres_file_writer.rb
|
159
89
|
- lib/mysql2postgres/postgres_writer.rb
|
160
90
|
- lib/mysql2postgres/version.rb
|
161
|
-
- lib/mysql2postgres/writer.rb
|
162
91
|
- mysql2postgres.gemspec
|
163
92
|
- test/fixtures/config_all_options.yml
|
93
|
+
- test/fixtures/config_min_options.yml
|
94
|
+
- test/fixtures/config_to_file.yml
|
164
95
|
- test/fixtures/seed_integration_tests.sql
|
165
96
|
- test/integration/convert_to_db_test.rb
|
166
97
|
- test/integration/convert_to_file_test.rb
|
167
98
|
- test/integration/converter_test.rb
|
168
|
-
- test/integration/
|
99
|
+
- test/integration/mysql_reader_connection_test.rb
|
169
100
|
- test/integration/mysql_reader_test.rb
|
170
|
-
- test/integration/
|
101
|
+
- test/integration/postgres_db_writer_test.rb
|
171
102
|
- test/test_helper.rb
|
103
|
+
- test/units/mysql_test.rb
|
172
104
|
- test/units/option_test.rb
|
173
105
|
- test/units/postgres_file_writer_test.rb
|
174
|
-
homepage: https://
|
106
|
+
homepage: https://github.com/AlphaNodes/mysql2postgres
|
175
107
|
licenses:
|
176
108
|
- MIT
|
177
109
|
metadata:
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require File.expand_path '../test_helper', __dir__
|
4
|
-
|
5
|
-
class PostgresDbWriterBaseTest < Test::Unit::TestCase
|
6
|
-
class << self
|
7
|
-
def startup
|
8
|
-
seed_test_database
|
9
|
-
@options = get_test_config_by_label :localmysql_to_db_convert_nothing
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_pg_connection
|
14
|
-
assert_nothing_raised do
|
15
|
-
mysql2postgres::PostgresDbWriter.new Tempfile.new('mysql2postgres_test_').path, @options
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|