mysql2postgres 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mysql2psql/postgres_writer'
4
+
5
+ class Mysql2psql
6
+ class PostgresFileWriter < PostgresWriter
7
+ def initialize(file)
8
+ super()
9
+
10
+ @f = File.open file, 'w+:UTF-8'
11
+ @f << <<~SQL_HEADER
12
+ -- MySQL 2 PostgreSQL dump\n
13
+ SET client_encoding = 'UTF8';
14
+ SET standard_conforming_strings = off;
15
+ SET check_function_bodies = false;
16
+ SET client_min_messages = warning;
17
+
18
+ SQL_HEADER
19
+ end
20
+
21
+ def truncate(table)
22
+ serial_key = nil
23
+ maxval = nil
24
+
25
+ table.columns.map do |column|
26
+ if column[:auto_increment]
27
+ serial_key = column[:name]
28
+ maxval = column[:maxval].to_i < 1 ? 1 : column[:maxval] + 1
29
+ end
30
+ end
31
+
32
+ @f << <<~SQL_TRUNCATE
33
+ -- TRUNCATE #{table.name};
34
+ TRUNCATE #{PG::Connection.quote_ident table.name} CASCADE;
35
+
36
+ SQL_TRUNCATE
37
+
38
+ return unless serial_key
39
+
40
+ @f << <<~SQL_SERIAL
41
+ SELECT pg_catalog.setval(pg_get_serial_sequence('#{table.name}', '#{serial_key}'), #{maxval}, true);
42
+ SQL_SERIAL
43
+ end
44
+
45
+ def write_table(table)
46
+ primary_keys = []
47
+ serial_key = nil
48
+ maxval = nil
49
+
50
+ columns = table.columns.map do |column|
51
+ if column[:auto_increment]
52
+ serial_key = column[:name]
53
+ maxval = column[:maxval].to_i < 1 ? 1 : column[:maxval] + 1
54
+ end
55
+ primary_keys << column[:name] if column[:primary_key]
56
+ " #{column_description column}"
57
+ end.join(",\n")
58
+
59
+ if serial_key
60
+ @f << <<~SQL_SEQUENCE
61
+ --
62
+ -- Name: #{table.name}_#{serial_key}_seq; Type: SEQUENCE; Schema: public
63
+ --
64
+
65
+ DROP SEQUENCE IF EXISTS #{table.name}_#{serial_key}_seq CASCADE;
66
+
67
+ CREATE SEQUENCE #{table.name}_#{serial_key}_seq
68
+ INCREMENT BY 1
69
+ NO MAXVALUE
70
+ NO MINVALUE
71
+ CACHE 1;
72
+
73
+
74
+ SELECT pg_catalog.setval('#{table.name}_#{serial_key}_seq', #{maxval}, true);
75
+
76
+ SQL_SEQUENCE
77
+ end
78
+
79
+ @f << <<~SQL_TABLE
80
+ -- Table: #{table.name}
81
+
82
+ -- DROP TABLE #{table.name};
83
+ DROP TABLE IF EXISTS #{PG::Connection.quote_ident table.name} CASCADE;
84
+
85
+ CREATE TABLE #{PG::Connection.quote_ident table.name} (
86
+ SQL_TABLE
87
+
88
+ @f << columns
89
+
90
+ if (primary_index = table.indexes.find { |index| index[:primary] })
91
+ @f << ",\n CONSTRAINT #{table.name}_pkey PRIMARY KEY(#{primary_index[:columns].map { |col| PG::Connection.quote_ident(col) }.join(', ')})"
92
+ end
93
+
94
+ @f << <<~SQL_OIDS
95
+ \n)
96
+ WITHOUT OIDS;
97
+ SQL_OIDS
98
+
99
+ table.indexes.each do |index|
100
+ next if index[:primary]
101
+
102
+ unique = index[:unique] ? 'UNIQUE ' : nil
103
+ @f << <<~SQL_INDEX
104
+ DROP INDEX IF EXISTS #{PG::Connection.quote_ident index[:name]} CASCADE;
105
+ CREATE #{unique}INDEX #{PG::Connection.quote_ident index[:name]}
106
+ ON #{PG::Connection.quote_ident table.name} (#{index[:columns].map { |col| PG::Connection.quote_ident(col) }.join(', ')});
107
+ SQL_INDEX
108
+ end
109
+ end
110
+
111
+ def write_indexes(_table); end
112
+
113
+ def write_constraints(table)
114
+ table.foreign_keys.each do |key|
115
+ @f << "ALTER TABLE #{PG::Connection.quote_ident table.name} " \
116
+ "ADD FOREIGN KEY (#{key[:column].map { |c| PG::Connection.quote_ident(c) }.join(', ')}) " \
117
+ "REFERENCES #{PG::Connection.quote_ident key[:ref_table]}(#{key[:ref_column].map { |c| PG::Connection.quote_ident(c) }.join(', ')}) " \
118
+ "ON UPDATE #{key[:on_update]} ON DELETE #{key[:on_delete]};\n"
119
+ end
120
+ end
121
+
122
+ def write_contents(table, reader)
123
+ @f << <<~SQL_COPY
124
+ --
125
+ -- Data for Name: #{table.name}; Type: TABLE DATA; Schema: public
126
+ --
127
+
128
+ COPY "#{table.name}" (#{table.columns.map { |column| PG::Connection.quote_ident(column[:name]) }.join(', ')}) FROM stdin;
129
+ SQL_COPY
130
+
131
+ reader.paginated_read table, 1000 do |row, _counter|
132
+ process_row table, row
133
+ @f << row.join("\t")
134
+ @f << "\n"
135
+ end
136
+ @f << "\\.\n\n"
137
+ end
138
+
139
+ def close
140
+ @f.close
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+ require 'mysql2psql/writer'
5
+
6
+ class Mysql2psql
7
+ class PostgresWriter < Writer
8
+ def column_description(column)
9
+ "#{PG::Connection.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
+ return "integer DEFAULT nextval('#{column[:table_name]}_#{column[:name]}_seq'::regclass) NOT NULL" if column[:auto_increment]
18
+
19
+ default = if column[:default]
20
+ " DEFAULT #{column[:default].nil? ? 'NULL' : "'#{PG::Connection.escape column[:default]}'"}"
21
+ end
22
+ null = column[:null] ? '' : ' NOT NULL'
23
+ type = case column[:type]
24
+ # String types
25
+ when 'char'
26
+ default += '::char' if default
27
+ "character(#{column[:length]})"
28
+ when 'varchar'
29
+ default += '::character varying' if default
30
+ "character varying(#{column[:length]})"
31
+ # Integer and numeric types
32
+ when 'integer'
33
+ default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_i}" if default
34
+ 'integer'
35
+ when 'bigint'
36
+ default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_i}" if default
37
+ 'bigint'
38
+ when 'tinyint'
39
+ default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_i}" if default
40
+ 'smallint'
41
+ when 'boolean'
42
+ default = " DEFAULT #{column[:default].to_i == 1 ? 'true' : 'false'}" if default
43
+ 'boolean'
44
+ when 'float', 'float unsigned'
45
+ default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_f}" if default
46
+ 'real'
47
+ when 'decimal'
48
+ default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default]}" if default
49
+ "numeric(#{column[:length] || 10}, #{column[:decimals] || 0})"
50
+ when 'double precision'
51
+ default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default]}" if default
52
+ 'double precision'
53
+ when 'datetime'
54
+ default = nil
55
+ 'timestamp without time zone'
56
+ when 'date'
57
+ default = nil
58
+ 'date'
59
+ when 'timestamp'
60
+ default = ' DEFAULT CURRENT_TIMESTAMP' if column[:default] == 'CURRENT_TIMESTAMP'
61
+ default = " DEFAULT '1970-01-01 00:00'" if column[:default] == '0000-00-00 00:00'
62
+ default = " DEFAULT '1970-01-01 00:00:00'" if column[:default] == '0000-00-00 00:00:00'
63
+ 'timestamp without time zone'
64
+ when 'time'
65
+ default = ' DEFAULT NOW()' if default
66
+ 'time without time zone'
67
+ when 'blob', 'longblob', 'mediumblob', 'tinyblob', 'varbinary'
68
+ 'bytea'
69
+ when 'text', 'tinytext', 'mediumtext', 'longtext'
70
+ 'text'
71
+ when /^enum/
72
+ default += '::character varying' if default
73
+ enum = column[:type].gsub(/enum|\(|\)/, '')
74
+ max_enum_size = enum.split(',').map { |check| check.size - 2 }.max
75
+ "character varying(#{max_enum_size}) check( \"#{column[:name]}\" in (#{enum}))"
76
+ when 'geometry', 'multipolygon'
77
+ 'geometry'
78
+ else
79
+ puts "Unknown #{column.inspect}"
80
+ column[:type].inspect
81
+ return ''
82
+ end
83
+
84
+ "#{type}#{default}#{null}"
85
+ end
86
+
87
+ def process_row(table, row)
88
+ table.columns.each_with_index do |column, index|
89
+ row[index] = Time.at(row[index]).utc.strftime('%H:%M:%S') if column[:type] == 'time' && row[index]
90
+
91
+ if row[index].is_a? Time
92
+ row[index] = row[index].to_s.gsub '0000-00-00 00:00', '1970-01-01 00:00'
93
+ row[index] = row[index].to_s.gsub '0000-00-00 00:00:00', '1970-01-01 00:00:00'
94
+ end
95
+
96
+ if column_type(column) == 'boolean'
97
+ row[index] = if row[index] == 1
98
+ 't'
99
+ else
100
+ row[index].zero? ? 'f' : row[index]
101
+ end
102
+ end
103
+
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
126
+
127
+ row[index] = '\N' unless row[index]
128
+ end
129
+ end
130
+
131
+ def truncate(_table) end
132
+ end
133
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Mysql2psql
4
+ VERSION = '0.3.2'
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Mysql2psql
4
+ class Writer
5
+ def inload
6
+ raise "Method 'inload' needs to be overridden..."
7
+ end
8
+ end
9
+ end
data/lib/mysql2psql.rb ADDED
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pg'
4
+ require 'pg_ext'
5
+ require 'pg/exceptions'
6
+ require 'pg/constants'
7
+ require 'pg/connection'
8
+ require 'pg/result'
9
+
10
+ require 'mysql2psql/version'
11
+ require 'mysql2psql/converter'
12
+ require 'mysql2psql/mysql_reader'
13
+ require 'mysql2psql/writer'
14
+ require 'mysql2psql/postgres_writer'
15
+ require 'mysql2psql/postgres_file_writer'
16
+ require 'mysql2psql/postgres_db_writer'
17
+
18
+ require 'debug' if ENV.fetch('ENABLE_DEBUG', nil) == '1'
19
+
20
+ class Mysql2psql
21
+ attr_reader :options, :reader, :writer
22
+
23
+ def initialize(yaml)
24
+ @options = build_options yaml
25
+ end
26
+
27
+ def build_options(yaml)
28
+ yaml.transform_keys(&:to_sym).tap do |opts|
29
+ opts[:mysql].transform_keys!(&:to_sym)
30
+ opts[:destination].transform_keys!(&:to_sym)
31
+ opts[:destination].each do |env, settings|
32
+ opts[:destination][env] = settings.transform_keys(&:to_sym)
33
+ end
34
+ end
35
+ end
36
+
37
+ def send_file_to_postgres(path)
38
+ connection = Connection.new options
39
+ connection.load_file path
40
+ end
41
+
42
+ def convert
43
+ @reader = MysqlReader.new options
44
+
45
+ tag = Time.new.strftime '%Y%m%d-%H%M%S'
46
+
47
+ path = './'
48
+ path = options[:dump_file_directory] if options[:dump_file_directory]
49
+
50
+ filename = File.expand_path File.join(path, "output_#{tag}.sql")
51
+ puts "Dumpfile: #{filename}"
52
+
53
+ @writer = PostgresDbWriter.new filename, options
54
+
55
+ Converter.new(reader, writer, options).convert
56
+
57
+ File.delete filename if options[:remove_dump_file] && File.exist?(filename)
58
+ end
59
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path '../lib', __FILE__
4
+ puts lib
5
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib
6
+ require 'mysql2psql/version'
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = 'mysql2postgres'
10
+ s.version = Mysql2psql::VERSION
11
+ s.licenses = ['MIT']
12
+
13
+ s.authors = [
14
+ 'Max Lapshin <max@maxidoors.ru>',
15
+ 'Anton Ageev <anton@ageev.name>',
16
+ 'Samuel Tribehou <cracoucax@gmail.com>',
17
+ 'Marco Nenciarini <marco.nenciarini@devise.it>',
18
+ 'James Nobis <jnobis@jnobis.controldocs.com>',
19
+ 'quel <github@quelrod.net>',
20
+ 'Holger Amann <keeney@fehu.org>',
21
+ 'Maxim Dobriakov <closer.main@gmail.com>',
22
+ 'Michael Kimsal <mgkimsal@gmail.com>',
23
+ 'Jacob Coby <jcoby@portallabs.com>',
24
+ 'Neszt Tibor <neszt@tvnetwork.hu>',
25
+ 'Miroslav Kratochvil <exa.exa@gmail.com>',
26
+ 'Paul Gallagher <gallagher.paul@gmail.com>',
27
+ 'Alex C Jokela <ajokela@umn.edu>',
28
+ 'Peter Clark <pclark@umn.edu>',
29
+ 'Juga Paazmaya <olavic@gmail.com>',
30
+ 'Alexander Meindl <a.meindl@alphanodes.com'
31
+ ]
32
+ s.description = 'Translates MySQL -> PostgreSQL'
33
+ s.email = 'a.meindl@alphanodes.com'
34
+ s.metadata = { 'rubygems_mfa_required' => 'true' }
35
+ s.executables = ['mysql2psql']
36
+ s.required_ruby_version = '>= 2.7'
37
+
38
+ s.files = [
39
+ '.gitignore',
40
+ 'MIT-LICENSE',
41
+ 'README.md',
42
+ 'Rakefile',
43
+ 'bin/mysql2psql',
44
+ 'lib/mysql2psql.rb',
45
+ 'lib/mysql2psql/converter.rb',
46
+ 'lib/mysql2psql/connection.rb',
47
+ 'lib/mysql2psql/mysql_reader.rb',
48
+ 'lib/mysql2psql/postgres_db_writer.rb',
49
+ 'lib/mysql2psql/postgres_file_writer.rb',
50
+ 'lib/mysql2psql/postgres_db_writer.rb',
51
+ 'lib/mysql2psql/postgres_writer.rb',
52
+ 'lib/mysql2psql/version.rb',
53
+ 'lib/mysql2psql/writer.rb',
54
+ 'mysql2postgres.gemspec',
55
+ 'test/fixtures/config_all_options.yml',
56
+ 'test/fixtures/seed_integration_tests.sql',
57
+ 'test/integration/convert_to_db_test.rb',
58
+ 'test/integration/convert_to_file_test.rb',
59
+ 'test/integration/converter_test.rb',
60
+ 'test/integration/mysql_reader_base_test.rb',
61
+ 'test/integration/mysql_reader_test.rb',
62
+ 'test/integration/postgres_db_writer_base_test.rb',
63
+ 'test/units/option_test.rb',
64
+ 'test/units/postgres_file_writer_test.rb',
65
+ 'test/test_helper.rb'
66
+ ]
67
+ s.homepage = 'https://code.alphanodes.com/alphanodes/mysql2psql'
68
+ s.rdoc_options = ['--charset=UTF-8']
69
+ s.require_paths = ['lib']
70
+ s.summary = 'MySQL to PostgreSQL Data Translation'
71
+
72
+ s.add_dependency 'rake'
73
+ s.add_runtime_dependency 'pg', '~> 1.2.2'
74
+ s.add_runtime_dependency 'postgres-pr', '~> 0.7'
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
+ end
@@ -0,0 +1,39 @@
1
+ mysql:
2
+ hostname: localhost
3
+ port: 3306
4
+ socket: /tmp/mysql.sock
5
+ username: somename
6
+ password: secretpassword
7
+ database: somename
8
+
9
+
10
+ destination:
11
+ # if file is given, output goes to file, else postgres
12
+ file: somefile
13
+ test:
14
+ hostname: localhost
15
+ port: 5432
16
+ username: somename
17
+ password: secretpassword
18
+ database: somename
19
+
20
+ # if tables is given, only the listed tables will be converted. leave empty to convert all tables.
21
+ tables:
22
+ - table1
23
+ - table2
24
+ - table3
25
+ - table4
26
+
27
+ # if exclude_tables is given, exclude the listed tables from the conversion.
28
+ exclude_tables:
29
+ - table5
30
+ - table6
31
+
32
+ # if suppress_data is true, only the schema definition will be exported/migrated, and not the data
33
+ suppress_data: true
34
+
35
+ # if suppress_ddl is true, only the data will be exported/imported, and not the schema
36
+ suppress_ddl: false
37
+
38
+ # if force_truncate is true, forces a table truncate before table loading
39
+ force_truncate: false
@@ -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,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path '../test_helper', __dir__
4
+
5
+ class ConvertToDbTest < Test::Unit::TestCase
6
+ class << self
7
+ def startup
8
+ seed_test_database
9
+ @options = get_test_config_by_label :localmysql_to_db_convert_all
10
+ @mysql2psql = Mysql2psql.new @options
11
+ @mysql2psql.convert
12
+ @mysql2psql.writer.open
13
+ end
14
+
15
+ def shutdown
16
+ @@mysql2psql.writer.close
17
+ end
18
+ end
19
+
20
+ def test_table_creation
21
+ assert_true @mysql2psql.writer.exists?('numeric_types_basics')
22
+ end
23
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path '../test_helper', __dir__
4
+
5
+ class ConvertToFileTest < Test::Unit::TestCase
6
+ class << self
7
+ def startup
8
+ seed_test_database
9
+ @options = get_test_config_by_label :localmysql_to_file_convert_all
10
+ @mysql2psql = Mysql2psql.new @options
11
+ @mysql2psql.convert
12
+ @content = File.read @mysql2psql.options[:destfile]
13
+ end
14
+ end
15
+
16
+ def content
17
+ @@content
18
+ end
19
+
20
+ def test_table_creation
21
+ assert_not_nil content.match('DROP TABLE IF EXISTS "numeric_types_basics" CASCADE')
22
+ assert_not_nil content.include?('CREATE TABLE "numeric_types_basics"')
23
+ end
24
+
25
+ def test_basic_numerics_tinyint
26
+ assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_tinyint" smallint,.*\)', Regexp::MULTILINE).match(content)
27
+ end
28
+
29
+ def test_basic_numerics_smallint
30
+ assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_smallint" integer,.*\)', Regexp::MULTILINE).match(content)
31
+ end
32
+
33
+ def test_basic_numerics_mediumint
34
+ assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_mediumint" integer,.*\)', Regexp::MULTILINE).match(content)
35
+ end
36
+
37
+ def test_basic_numerics_int
38
+ assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_int" integer,.*\)', Regexp::MULTILINE).match(content)
39
+ end
40
+
41
+ def test_basic_numerics_integer
42
+ assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_integer" integer,.*\)', Regexp::MULTILINE).match(content)
43
+ end
44
+
45
+ def test_basic_numerics_bigint
46
+ assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_bigint" bigint,.*\)', Regexp::MULTILINE).match(content)
47
+ end
48
+
49
+ def test_basic_numerics_real
50
+ assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_real" double precision,.*\)', Regexp::MULTILINE).match(content)
51
+ end
52
+
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
+
57
+ def test_basic_numerics_float
58
+ assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_float" double precision,.*\)', Regexp::MULTILINE).match(content)
59
+ end
60
+
61
+ def test_basic_numerics_decimal
62
+ assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_decimal" numeric\(10, 0\),.*\)', Regexp::MULTILINE).match(content)
63
+ end
64
+
65
+ 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).match(content)
67
+ end
68
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path '../test_helper', __dir__
4
+
5
+ class ConverterTest < Test::Unit::TestCase
6
+ class << self
7
+ def startup
8
+ seed_test_database
9
+ @options = get_test_config_by_label :localmysql_to_file_convert_nothing
10
+ end
11
+ end
12
+
13
+ def test_new_converter
14
+ assert_nothing_raised do
15
+ reader = get_test_reader @options
16
+ writer = get_test_file_writer @options
17
+ converter = Mysql2psql::Converter.new reader, writer, @options
18
+ assert_equal 0, converter.convert
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path '../test_helper', __dir__
4
+
5
+ class MysqlReaderBaseTest < Test::Unit::TestCase
6
+ class << self
7
+ def startup
8
+ seed_test_database
9
+ @options = get_test_config_by_label :localmysql_to_file_convert_nothing
10
+ end
11
+ end
12
+
13
+ def test_mysql_connection
14
+ assert_nothing_raised do
15
+ Mysql2psql::MysqlReader.new @options
16
+ end
17
+ end
18
+
19
+ def test_mysql_reconnect
20
+ assert_nothing_raised do
21
+ reader = Mysql2psql::MysqlReader.new @options
22
+ reader.reconnect
23
+ end
24
+ end
25
+
26
+ def test_mysql_connection_without_port
27
+ assert_nothing_raised do
28
+ @options[:mysql][:port] = ''
29
+ @options[:mysql][:socket] = ''
30
+ Mysql2psql::MysqlReader.new @options
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path '../test_helper', __dir__
4
+
5
+ class MysqlReaderTest < Test::Unit::TestCase
6
+ class << self
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
11
+ end
12
+ end
13
+
14
+ def test_db_connection
15
+ assert_nothing_raised do
16
+ @reader.mysql.ping
17
+ end
18
+ end
19
+
20
+ def test_tables_collection
21
+ values = @reader.tables.select { |t| t.name == 'numeric_types_basics' }
22
+ assert_true values.length == 1
23
+ assert_equal 'numeric_types_basics', values[0].name
24
+ end
25
+
26
+ def test_paginated_read
27
+ expected_rows = 3
28
+ page_size = 2
29
+ expected_pages = (1.0 * expected_rows / page_size).ceil
30
+
31
+ row_count = my_row_count = 0
32
+ table = @reader.tables.find { |t| t.name == 'numeric_types_basics' }
33
+ @reader.paginated_read table, page_size do |_row, counter|
34
+ row_count = counter
35
+ my_row_count += 1
36
+ end
37
+ assert_equal expected_rows, row_count
38
+ assert_equal expected_rows, my_row_count
39
+ end
40
+ end