mysql2psql 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,9 @@
1
+ class Mysql2psql
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ PATCH = 0
6
+
7
+ STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ class Mysql2psql
2
+
3
+ class Writer
4
+ end
5
+
6
+ end
@@ -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