mysql2postgres 0.3.2

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.
@@ -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