mysql2psql 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.
- data/.gitignore +4 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +135 -0
- data/Rakefile +87 -0
- data/bin/mysql2psql +7 -0
- data/lib/mysql2psql.rb +41 -0
- data/lib/mysql2psql/config.rb +100 -0
- data/lib/mysql2psql/config_base.rb +39 -0
- data/lib/mysql2psql/converter.rb +55 -0
- data/lib/mysql2psql/errors.rb +16 -0
- data/lib/mysql2psql/mysql_reader.rb +191 -0
- data/lib/mysql2psql/postgres_db_writer.rb +179 -0
- data/lib/mysql2psql/postgres_file_writer.rb +142 -0
- data/lib/mysql2psql/postgres_writer.rb +143 -0
- data/lib/mysql2psql/version.rb +9 -0
- data/lib/mysql2psql/writer.rb +6 -0
- data/mysql2psql.gemspec +91 -0
- data/test/fixtures/seed_integration_tests.sql +24 -0
- data/test/integration/convert_to_db_test.rb +29 -0
- data/test/integration/convert_to_file_test.rb +66 -0
- data/test/integration/converter_test.rb +34 -0
- data/test/integration/mysql_reader_base_test.rb +35 -0
- data/test/integration/mysql_reader_test.rb +47 -0
- data/test/integration/postgres_db_writer_base_test.rb +30 -0
- data/test/lib/ext_test_unit.rb +30 -0
- data/test/lib/test_helper.rb +88 -0
- data/test/units/config_base_test.rb +49 -0
- data/test/units/config_test.rb +31 -0
- data/test/units/postgres_file_writer_test.rb +29 -0
- metadata +167 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'pg'
|
2
|
+
|
3
|
+
require 'mysql2psql/writer'
|
4
|
+
|
5
|
+
class Mysql2psql
|
6
|
+
|
7
|
+
class PostgresWriter < Writer
|
8
|
+
def column_description(column)
|
9
|
+
"#{PGconn.quote_ident(column[:name])} #{column_type_info(column)}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def column_type(column)
|
13
|
+
column_type_info(column).split(" ").first
|
14
|
+
end
|
15
|
+
|
16
|
+
def column_type_info(column)
|
17
|
+
if column[:auto_increment]
|
18
|
+
return "integer DEFAULT nextval('#{column[:table_name]}_#{column[:name]}_seq'::regclass) NOT NULL"
|
19
|
+
end
|
20
|
+
|
21
|
+
default = column[:default] ? " DEFAULT #{column[:default] == nil ? 'NULL' : "'"+PGconn.escape(column[:default])+"'"}" : nil
|
22
|
+
null = column[:null] ? "" : " NOT NULL"
|
23
|
+
type =
|
24
|
+
case column[:type]
|
25
|
+
|
26
|
+
# String types
|
27
|
+
when "char"
|
28
|
+
default = default + "::char" if default
|
29
|
+
"character(#{column[:length]})"
|
30
|
+
when "varchar"
|
31
|
+
default = default + "::character varying" if default
|
32
|
+
# puts "VARCHAR: #{column.inspect}"
|
33
|
+
"character varying(#{column[:length]})"
|
34
|
+
|
35
|
+
# Integer and numeric types
|
36
|
+
when "integer"
|
37
|
+
default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_i}" if default
|
38
|
+
"integer"
|
39
|
+
when "bigint"
|
40
|
+
default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_i}" if default
|
41
|
+
"bigint"
|
42
|
+
when "tinyint"
|
43
|
+
default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_i}" if default
|
44
|
+
"smallint"
|
45
|
+
|
46
|
+
when "boolean"
|
47
|
+
default = " DEFAULT #{column[:default].to_i == 1 ? 'true' : 'false'}" if default
|
48
|
+
"boolean"
|
49
|
+
when "float"
|
50
|
+
default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_f}" if default
|
51
|
+
"real"
|
52
|
+
when "float unsigned"
|
53
|
+
default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_f}" if default
|
54
|
+
"real"
|
55
|
+
when "decimal"
|
56
|
+
default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default]}" if default
|
57
|
+
"numeric(#{column[:length] || 10}, #{column[:decimals] || 0})"
|
58
|
+
|
59
|
+
when "double precision"
|
60
|
+
default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default]}" if default
|
61
|
+
"double precision"
|
62
|
+
|
63
|
+
# Mysql datetime fields
|
64
|
+
when "datetime"
|
65
|
+
default = nil
|
66
|
+
"timestamp without time zone"
|
67
|
+
when "date"
|
68
|
+
default = nil
|
69
|
+
"date"
|
70
|
+
when "timestamp"
|
71
|
+
default = " DEFAULT CURRENT_TIMESTAMP" if column[:default] == "CURRENT_TIMESTAMP"
|
72
|
+
default = " DEFAULT '1970-01-01 00:00'" if column[:default] == "0000-00-00 00:00"
|
73
|
+
default = " DEFAULT '1970-01-01 00:00:00'" if column[:default] == "0000-00-00 00:00:00"
|
74
|
+
"timestamp without time zone"
|
75
|
+
when "time"
|
76
|
+
default = " DEFAULT NOW()" if default
|
77
|
+
"time without time zone"
|
78
|
+
|
79
|
+
when "tinyblob"
|
80
|
+
"bytea"
|
81
|
+
when "mediumblob"
|
82
|
+
"bytea"
|
83
|
+
when "longblob"
|
84
|
+
"bytea"
|
85
|
+
when "blob"
|
86
|
+
"bytea"
|
87
|
+
when "varbinary"
|
88
|
+
"bytea"
|
89
|
+
when "tinytext"
|
90
|
+
"text"
|
91
|
+
when "mediumtext"
|
92
|
+
"text"
|
93
|
+
when "longtext"
|
94
|
+
"text"
|
95
|
+
when "text"
|
96
|
+
"text"
|
97
|
+
when /^enum/
|
98
|
+
default = default + "::character varying" if default
|
99
|
+
enum = column[:type].gsub(/enum|\(|\)/, '')
|
100
|
+
max_enum_size = enum.split(',').map{ |check| check.size() -2}.sort[-1]
|
101
|
+
"character varying(#{max_enum_size}) check( #{column[:name]} in (#{enum}))"
|
102
|
+
else
|
103
|
+
puts "Unknown #{column.inspect}"
|
104
|
+
column[:type].inspect
|
105
|
+
return ""
|
106
|
+
end
|
107
|
+
"#{type}#{default}#{null}"
|
108
|
+
end
|
109
|
+
|
110
|
+
def process_row(table, row)
|
111
|
+
table.columns.each_with_index do |column, index|
|
112
|
+
|
113
|
+
if column[:type] == "time"
|
114
|
+
row[index] = "%02d:%02d:%02d" % [row[index].hour, row[index].minute, row[index].second]
|
115
|
+
end
|
116
|
+
|
117
|
+
if row[index].is_a?(Mysql::Time)
|
118
|
+
row[index] = row[index].to_s.gsub('0000-00-00 00:00', '1970-01-01 00:00')
|
119
|
+
row[index] = row[index].to_s.gsub('0000-00-00 00:00:00', '1970-01-01 00:00:00')
|
120
|
+
end
|
121
|
+
|
122
|
+
if column_type(column) == "boolean"
|
123
|
+
row[index] = row[index] == 1 ? 't' : row[index] == 0 ? 'f' : row[index]
|
124
|
+
end
|
125
|
+
|
126
|
+
if row[index].is_a?(String)
|
127
|
+
if column_type(column) == "bytea"
|
128
|
+
row[index] = PGconn.escape_bytea(row[index])
|
129
|
+
else
|
130
|
+
row[index] = row[index].gsub(/\\/, '\\\\\\').gsub(/\n/,'\n').gsub(/\t/,'\t').gsub(/\r/,'\r').gsub(/\0/, '')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
row[index] = '\N' if !row[index]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def truncate(table)
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
data/mysql2psql.gemspec
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{mysql2psql}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Max Lapshin <max@maxidoors.ru>", "Anton Ageev <anton@ageev.name>", "Samuel Tribehou <cracoucax@gmail.com>", "Marco Nenciarini <marco.nenciarini@devise.it>", "James Nobis <jnobis@jnobis.controldocs.com>", "quel <github@quelrod.net>", "Holger Amann <keeney@fehu.org>", "Maxim Dobriakov <closer.main@gmail.com>", "Michael Kimsal <mgkimsal@gmail.com>", "Jacob Coby <jcoby@portallabs.com>", "Neszt Tibor <neszt@tvnetwork.hu>", "Miroslav Kratochvil <exa.exa@gmail.com>", "Paul Gallagher <gallagher.paul@gmail.com>"]
|
12
|
+
s.date = %q{2010-09-19}
|
13
|
+
s.default_executable = %q{mysql2psql}
|
14
|
+
s.description = %q{It can create postgresql dump from mysql database or directly load data from mysql to
|
15
|
+
postgresql (at about 100 000 records per minute). Translates most data types and indexes.}
|
16
|
+
s.email = %q{gallagher.paul@gmail.com}
|
17
|
+
s.executables = ["mysql2psql"]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".gitignore",
|
23
|
+
"MIT-LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"bin/mysql2psql",
|
27
|
+
"lib/mysql2psql.rb",
|
28
|
+
"lib/mysql2psql/config.rb",
|
29
|
+
"lib/mysql2psql/config_base.rb",
|
30
|
+
"lib/mysql2psql/converter.rb",
|
31
|
+
"lib/mysql2psql/errors.rb",
|
32
|
+
"lib/mysql2psql/mysql_reader.rb",
|
33
|
+
"lib/mysql2psql/postgres_db_writer.rb",
|
34
|
+
"lib/mysql2psql/postgres_file_writer.rb",
|
35
|
+
"lib/mysql2psql/postgres_writer.rb",
|
36
|
+
"lib/mysql2psql/version.rb",
|
37
|
+
"lib/mysql2psql/writer.rb",
|
38
|
+
"mysql2psql.gemspec",
|
39
|
+
"test/fixtures/config_all_options.yml",
|
40
|
+
"test/fixtures/seed_integration_tests.sql",
|
41
|
+
"test/integration/convert_to_db_test.rb",
|
42
|
+
"test/integration/convert_to_file_test.rb",
|
43
|
+
"test/integration/converter_test.rb",
|
44
|
+
"test/integration/mysql_reader_base_test.rb",
|
45
|
+
"test/integration/mysql_reader_test.rb",
|
46
|
+
"test/integration/postgres_db_writer_base_test.rb",
|
47
|
+
"test/lib/ext_test_unit.rb",
|
48
|
+
"test/lib/test_helper.rb",
|
49
|
+
"test/units/config_base_test.rb",
|
50
|
+
"test/units/config_test.rb",
|
51
|
+
"test/units/postgres_file_writer_test.rb"
|
52
|
+
]
|
53
|
+
s.homepage = %q{http://github.com/tardate/mysql2postgresql}
|
54
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
55
|
+
s.require_paths = ["lib"]
|
56
|
+
s.rubygems_version = %q{1.3.7}
|
57
|
+
s.summary = %q{Tool for converting mysql database to postgresql}
|
58
|
+
s.test_files = [
|
59
|
+
"test/integration/convert_to_db_test.rb",
|
60
|
+
"test/integration/convert_to_file_test.rb",
|
61
|
+
"test/integration/converter_test.rb",
|
62
|
+
"test/integration/mysql_reader_base_test.rb",
|
63
|
+
"test/integration/mysql_reader_test.rb",
|
64
|
+
"test/integration/postgres_db_writer_base_test.rb",
|
65
|
+
"test/lib/ext_test_unit.rb",
|
66
|
+
"test/lib/test_helper.rb",
|
67
|
+
"test/units/config_base_test.rb",
|
68
|
+
"test/units/config_test.rb",
|
69
|
+
"test/units/postgres_file_writer_test.rb"
|
70
|
+
]
|
71
|
+
|
72
|
+
if s.respond_to? :specification_version then
|
73
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
74
|
+
s.specification_version = 3
|
75
|
+
|
76
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
77
|
+
s.add_runtime_dependency(%q<mysql>, ["= 2.8.1"])
|
78
|
+
s.add_runtime_dependency(%q<pg>, ["= 0.9.0"])
|
79
|
+
s.add_development_dependency(%q<test-unit>, [">= 2.1.1"])
|
80
|
+
else
|
81
|
+
s.add_dependency(%q<mysql>, ["= 2.8.1"])
|
82
|
+
s.add_dependency(%q<pg>, ["= 0.9.0"])
|
83
|
+
s.add_dependency(%q<test-unit>, [">= 2.1.1"])
|
84
|
+
end
|
85
|
+
else
|
86
|
+
s.add_dependency(%q<mysql>, ["= 2.8.1"])
|
87
|
+
s.add_dependency(%q<pg>, ["= 0.9.0"])
|
88
|
+
s.add_dependency(%q<test-unit>, [">= 2.1.1"])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
-- seed data for integration tests
|
2
|
+
|
3
|
+
DROP TABLE IF EXISTS numeric_types_basics;
|
4
|
+
CREATE TABLE numeric_types_basics (
|
5
|
+
id int,
|
6
|
+
f_tinyint TINYINT,
|
7
|
+
f_smallint SMALLINT,
|
8
|
+
f_mediumint MEDIUMINT,
|
9
|
+
f_int INT,
|
10
|
+
f_integer INTEGER,
|
11
|
+
f_bigint BIGINT,
|
12
|
+
f_real REAL,
|
13
|
+
f_double DOUBLE,
|
14
|
+
f_float FLOAT,
|
15
|
+
f_decimal DECIMAL,
|
16
|
+
f_numeric NUMERIC
|
17
|
+
);
|
18
|
+
|
19
|
+
INSERT INTO numeric_types_basics VALUES
|
20
|
+
(1,1,1,1,1,1,1,1,1,1,1,1),
|
21
|
+
(2,2,2,2,2,2,2,2,2,2,2,2),
|
22
|
+
(23,23,23,23,23,23,23,23,23,23,23,23);
|
23
|
+
|
24
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'mysql2psql'
|
4
|
+
|
5
|
+
class ConvertToDbTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def startup
|
9
|
+
seed_test_database
|
10
|
+
@@options=get_test_config_by_label(:localmysql_to_db_convert_all)
|
11
|
+
@@mysql2psql = Mysql2psql.new([@@options.filepath])
|
12
|
+
@@mysql2psql.convert
|
13
|
+
@@mysql2psql.writer.open
|
14
|
+
end
|
15
|
+
def shutdown
|
16
|
+
@@mysql2psql.writer.close
|
17
|
+
delete_files_for_test_config(@@options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
def setup
|
21
|
+
end
|
22
|
+
def teardown
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_table_creation
|
26
|
+
assert_true @@mysql2psql.writer.exists?('numeric_types_basics')
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'mysql2psql'
|
4
|
+
|
5
|
+
class ConvertToFileTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def startup
|
9
|
+
seed_test_database
|
10
|
+
@@options=get_test_config_by_label(:localmysql_to_file_convert_all)
|
11
|
+
@@mysql2psql = Mysql2psql.new([@@options.filepath])
|
12
|
+
@@mysql2psql.convert
|
13
|
+
@@content = IO.read(@@mysql2psql.options.destfile)
|
14
|
+
end
|
15
|
+
def shutdown
|
16
|
+
delete_files_for_test_config(@@options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
def setup
|
20
|
+
end
|
21
|
+
def teardown
|
22
|
+
end
|
23
|
+
def content
|
24
|
+
@@content
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_table_creation
|
28
|
+
assert_not_nil content.match('DROP TABLE IF EXISTS "numeric_types_basics" CASCADE')
|
29
|
+
assert_not_nil content.match(/CREATE TABLE "numeric_types_basics"/)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_basic_numerics_tinyint
|
33
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_tinyint" smallint,.*\)', Regexp::MULTILINE).match( content )
|
34
|
+
end
|
35
|
+
def test_basic_numerics_smallint
|
36
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_smallint" integer,.*\)', Regexp::MULTILINE).match( content )
|
37
|
+
end
|
38
|
+
def test_basic_numerics_mediumint
|
39
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_mediumint" integer,.*\)', Regexp::MULTILINE).match( content )
|
40
|
+
end
|
41
|
+
def test_basic_numerics_int
|
42
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_int" integer,.*\)', Regexp::MULTILINE).match( content )
|
43
|
+
end
|
44
|
+
def test_basic_numerics_integer
|
45
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_integer" integer,.*\)', Regexp::MULTILINE).match( content )
|
46
|
+
end
|
47
|
+
def test_basic_numerics_bigint
|
48
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_bigint" bigint,.*\)', Regexp::MULTILINE).match( content )
|
49
|
+
end
|
50
|
+
def test_basic_numerics_real
|
51
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_real" double precision,.*\)', Regexp::MULTILINE).match( content )
|
52
|
+
end
|
53
|
+
def test_basic_numerics_double
|
54
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_double" double precision,.*\)', Regexp::MULTILINE).match( content )
|
55
|
+
end
|
56
|
+
def test_basic_numerics_float
|
57
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_float" numeric\(20, 0\),.*\)', Regexp::MULTILINE).match( content )
|
58
|
+
end
|
59
|
+
def test_basic_numerics_decimal
|
60
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_decimal" numeric\(10, 0\),.*\)', Regexp::MULTILINE).match( content )
|
61
|
+
end
|
62
|
+
def test_basic_numerics_numeric
|
63
|
+
assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_numeric" numeric\(10, 0\)[\w\n]*\)', Regexp::MULTILINE).match( content )
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'mysql2psql/converter'
|
4
|
+
|
5
|
+
class ConverterTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def startup
|
9
|
+
seed_test_database
|
10
|
+
@@options=get_test_config_by_label(:localmysql_to_file_convert_nothing)
|
11
|
+
end
|
12
|
+
def shutdown
|
13
|
+
delete_files_for_test_config(@@options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def setup
|
17
|
+
end
|
18
|
+
def teardown
|
19
|
+
end
|
20
|
+
def options
|
21
|
+
@@options
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_new_converter
|
25
|
+
assert_nothing_raised do
|
26
|
+
reader=get_test_reader(options)
|
27
|
+
writer=get_test_file_writer(options)
|
28
|
+
converter=Mysql2psql::Converter.new(reader,writer,options)
|
29
|
+
assert_equal 0,converter.convert
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'mysql2psql/mysql_reader'
|
4
|
+
|
5
|
+
class MysqlReaderBaseTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def startup
|
9
|
+
seed_test_database
|
10
|
+
@@options = get_test_config_by_label(:localmysql_to_file_convert_nothing)
|
11
|
+
end
|
12
|
+
def shutdown
|
13
|
+
delete_files_for_test_config(@@options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def setup
|
17
|
+
end
|
18
|
+
def teardown
|
19
|
+
end
|
20
|
+
def options
|
21
|
+
@@options
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_mysql_connection
|
25
|
+
assert_nothing_raised do
|
26
|
+
reader = Mysql2psql::MysqlReader.new(options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
def test_mysql_reconnect
|
30
|
+
assert_nothing_raised do
|
31
|
+
reader = Mysql2psql::MysqlReader.new(options)
|
32
|
+
reader.reconnect
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|