diff --git a/.gitignore b/.gitignore index dc4ef0d..42965bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ -test/fixtures/test*.sql -config/database.yml -pkg -.rvmrc -.bundle +*.gem *.sql +.bundle +.rvmrc +config/database.yml Gemfile.lock +pkg +test/fixtures/test*.sql diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c686469 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +--- +os: linux +dist: xenial + +language: ruby +cache: bundler +rvm: + - 2.7.1 + - 2.6.6 + - 2.5.8 + - 2.4.10 + - 2.3.8 + - 2.2.10 + - 2.1.10 + - ruby-head + - jruby-9.2.13.0 + +jobs: + allow_failures: + - rvm: ruby-head diff --git a/Gemfile b/Gemfile index de91544..6693ca0 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,8 @@ -source :rubygems +source 'https://rubygems.org' -gem 'rake', '~> 10.0' -gem 'mysql-pr' -gem 'postgres-pr' +gem 'rake', '~> 10.3' +gem 'mysql-pr', '~> 2.9' +gem 'postgres-pr', '~> 0.6' platforms :jruby do gem 'activerecord' @@ -11,8 +11,12 @@ platforms :jruby do gem 'activerecord-jdbcpostgresql-adapter' end -platforms :mri_19 do - gem 'pg' +platforms :mri do + gem 'pg', '~> 0.18' end -gem 'test-unit' +gem 'test-unit', '~> 2.1' + +group :test do + gem 'jeweler', '~> 2.0' +end diff --git a/MIT-LICENSE b/MIT-LICENSE index ec5684c..2c52b5a 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009-2010 name +Copyright (c) 2012-2013 Regents of the University of Minnesota Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 3c93deb..4dd5844 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,114 @@ # mysql-to-postgres - MySQL to PostgreSQL Data Translation -MRI or jruby supported. - -With a bit of a modified rails database.yml configuration, you can integrate mysql-to-postgres into a project. - -Sample Configuration file: - - default: &default - adapter: jdbcpostgresql - encoding: unicode - pool: 4 - username: terrapotamus - password: default - host: 127.0.0.1 - - development: &development - <<: *default - database: default_development - - test: &test - <<: *default - database: default_test - - production: &production - <<: *default - database: default_production - - mysql_data_source: &pii - hostname: localhost - port: 3306 - username: username - password: default - database: awesome_possum - - mysql2psql: - mysql: - <<: *pii - - destination: - production: - <<: *production - test: - <<: *test - development: - <<: *development - - tables: - - countries - - samples - - universes - - variable_groups - - variables - - sample_variables - - # if suppress_data is true, only the schema definition will be exported/migrated, and not the data - suppress_data: false - - # if suppress_ddl is true, only the data will be exported/imported, and not the schema - suppress_ddl: true - - # if force_truncate is true, forces a table truncate before table loading - force_truncate: false - - preserve_order: true - - remove_dump_file: true - - report_status: json # false, json, xml +[![Build Status](https://travis-ci.org/maxlapshin/mysql2postgres.svg)](https://travis-ci.org/maxlapshin/mysql2postgres) +[![Dependency Status](https://gemnasium.com/maxlapshin/mysql2postgres.svg)](https://gemnasium.com/maxlapshin/mysql2postgres) +MRI or jruby supported. The minimum Ruby version supported in `master` branch is `2.1.7`, +and the next release will have the same requirement. + +With a bit of a modified rails `database.yml` configuration, you can integrate `mysql-to-postgres`into a project. + +## Installation + +**Currently failing, see #81...** + +### Via RubyGems + +```sh +gem install mysqltopostgres +``` + +### From source + +```sh +git clone https://github.com/maxlapshin/mysql2postgres.git +cd mysql2postgres +bundle install +gem build mysqltopostgres.gemspec +sudo gem install mysqltopostgres-0.3.1.gem +``` + +## Sample Configuration + +Configuration is written in [YAML format](http://www.yaml.org/ "YAML Ain't Markup Language") +and passed as the first argument on the command line. + +```yaml +default: &default + adapter: jdbcpostgresql + encoding: unicode + pool: 4 + username: terrapotamus + password: default + host: 127.0.0.1 + +development: &development + <<: *default + database: default_development + +test: &test + <<: *default + database: default_test + +production: &production + <<: *default + database: default_production + +mysql_data_source: &pii + host: localhost + port: 3306 + socket: /tmp/mysqld.sock + username: username + password: default + database: awesome_possum + +mysql2psql: + mysql: + <<: *pii + + destination: + production: + <<: *production + test: + <<: *test + development: + <<: *development + + tables: + - countries + - samples + - universes + - variable_groups + - variables + - sample_variables + + # If suppress_data is true, only the schema definition will be exported/migrated, and not the data + suppress_data: false + + # If suppress_ddl is true, only the data will be exported/imported, and not the schema + suppress_ddl: true + + # If force_truncate is true, forces a table truncate before table loading + force_truncate: false + + preserve_order: true + + remove_dump_file: true + + dump_file_directory: /tmp + + report_status: json # false, json, xml + + # If clear_schema is true, the public schema will be recreated before conversion + # The import will fail if both clear_schema and suppress_ddl are true. + clear_schema: false +``` + +Please note that the MySQL connection will be using socket in case the host is not defined (`nil`) or it is `'localhost'`. + +## Testing + + +## License + +Licensed under [the MIT license](MIT-LICENSE). diff --git a/Rakefile b/Rakefile index 1486830..034abe6 100644 --- a/Rakefile +++ b/Rakefile @@ -1,42 +1,42 @@ require 'rubygems' require 'rake' -$LOAD_PATH.unshift('lib') -require 'mysql2psql/version' +require_relative 'lib/mysql2psql/version' begin require 'jeweler' Jeweler::Tasks.new do |gem| - gem.name = "mysql2psql" + gem.name = 'mysql2psql' gem.version = Mysql2psql::Version::STRING - gem.summary = %Q{Tool for converting mysql database to postgresql} - gem.description = %Q{It can create postgresql dump from mysql database or directly load data from mysql to + gem.summary = %(Tool for converting mysql database to postgresql) + gem.description = %{It can create postgresql dump from mysql database or directly load data from mysql to postgresql (at about 100 000 records per minute). Translates most data types and indexes.} - gem.email = "gallagher.paul@gmail.com" - gem.homepage = "http://github.com/tardate/mysql2postgresql" + gem.email = 'gallagher.paul@gmail.com' + gem.homepage = 'http://github.com/tardate/mysql2postgresql' gem.authors = [ - "Max Lapshin ", - "Anton Ageev ", - "Samuel Tribehou ", - "Marco Nenciarini ", - "James Nobis ", - "quel ", - "Holger Amann ", - "Maxim Dobriakov ", - "Michael Kimsal ", - "Jacob Coby ", - "Neszt Tibor ", - "Miroslav Kratochvil ", - "Paul Gallagher " - ] - gem.add_dependency "mysql", "= 2.8.1" - gem.add_dependency "pg", "= 0.9.0" - gem.add_development_dependency "test-unit", ">= 2.1.1" + 'Max Lapshin ', + 'Anton Ageev ', + 'Samuel Tribehou ', + 'Marco Nenciarini ', + 'James Nobis ', + 'quel ', + 'Holger Amann ', + 'Maxim Dobriakov ', + 'Michael Kimsal ', + 'Jacob Coby ', + 'Neszt Tibor ', + 'Miroslav Kratochvil ', + 'Paul Gallagher ', + 'Juga Paazmaya ' + ] + gem.add_dependency 'mysql-pr', '~> 2.9' + gem.add_dependency 'postgres-pr', '~> 0.6' + gem.add_development_dependency 'test-unit', '~> 2.1' # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings end Jeweler::GemcutterTasks.new rescue LoadError - puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler" + puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler' end require 'rake/testtask' @@ -54,10 +54,10 @@ namespace :test do end end -desc "Run all tests" +desc 'Run all tests' task :test do Rake::Task['test:units'].invoke - Rake::Task['test:integration'].invoke + #Rake::Task['test:integration'].invoke end begin @@ -69,15 +69,14 @@ begin end rescue LoadError task :rcov do - abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" + abort 'RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov' end end - -task :default => :test +task default: :test require 'rdoc/task' -RDoc::Task.new do |rdoc| +Rake::RDocTask.new do |rdoc| version = Mysql2psql::Version::STRING rdoc.rdoc_dir = 'rdoc' diff --git a/bin/mysql-to-postgres b/bin/mysql-to-postgres deleted file mode 100755 index a96ef4c..0000000 --- a/bin/mysql-to-postgres +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env ruby -$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib') - -require 'rubygems' -require 'bundler/setup' - -require 'mysql-to-postgres' - -CONFIG_FILE = File.expand_path(File.expand_path(File.dirname(__FILE__)) + "/../config/database.yml") - -if FileTest.exist?(CONFIG_FILE) or (ARGV.length > 0 and FileTest.exist?(File.expand_path(ARGV[0]))) - - if ARGV.length > 0 - file = ARGV[0] - else - file = CONFIG_FILE - end - - db_yaml = YAML::load_file file - - if db_yaml.has_key?('mysql2psql') - # puts db_yaml["mysql2psql"].to_s - Mysql2psql.new(db_yaml["mysql2psql"]).convert - else - # Oh Noes! There is no key in the hash... - raise "'#{file}' does not contain a configuration directive for mysql -> postgres" - end - -else - raise "'#{file}' does not exist" -end diff --git a/bin/mysqltopostgres b/bin/mysqltopostgres new file mode 100755 index 0000000..0ea8322 --- /dev/null +++ b/bin/mysqltopostgres @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby +$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib') + +require 'rubygems' +require 'bundler/setup' + +require 'mysqltopostgres' + +CONFIG_FILE = File.expand_path(File.expand_path(File.dirname(__FILE__)) + '/../config/database.yml') + +if FileTest.exist?(CONFIG_FILE) || (ARGV.length > 0 && FileTest.exist?(File.expand_path(ARGV[0]))) + + if ARGV.length > 0 + file = ARGV[0] + else + file = CONFIG_FILE + end + + db_yaml = YAML.load_file file + + if db_yaml.key?('mysql2psql') + # puts db_yaml["mysql2psql"].to_s + Mysql2psql.new(db_yaml['mysql2psql']).convert + else + # Oh Noes! There is no key in the hash... + fail "'#{file}' does not contain a configuration directive for mysql -> postgres" + end + +else + fail "'#{file}' does not exist" +end diff --git a/config/default.database.yml b/config/default.database.yml index dd78e60..1fcfaac 100644 --- a/config/default.database.yml +++ b/config/default.database.yml @@ -5,7 +5,9 @@ default: &default username: terrapotamus password: default host: 127.0.0.1 - + template: template_gis + gis_schema_name: gis_tmp + development: &development <<: *default database: default_development @@ -28,15 +30,15 @@ mysql_data_source: &pii mysql2psql: mysql: <<: *pii - + destination: production: <<: *production - test: + test: <<: *test development: <<: *development - + tables: - countries - samples @@ -45,17 +47,23 @@ mysql2psql: - variables - sample_variables - # if suppress_data is true, only the schema definition will be exported/migrated, and not the data + # If suppress_data is true, only the schema definition will be exported/migrated, and not the data suppress_data: false - # if suppress_ddl is true, only the data will be exported/imported, and not the schema + # If suppress_ddl is true, only the data will be exported/imported, and not the schema suppress_ddl: true - # if force_truncate is true, forces a table truncate before table loading + # If force_truncate is true, forces a table truncate before table loading force_truncate: false preserve_order: true remove_dump_file: true - + + dump_file_directory: /tmp + report_status: json # false, json, xml + + # If clear_schema is true, the public schema will be recreated before conversion + # The import will fail if both clear_schema and suppress_ddl are true. + clear_schema: false diff --git a/lib/mysql2psql/config.rb b/lib/mysql2psql/config.rb index 233e160..7da0a55 100644 --- a/lib/mysql2psql/config.rb +++ b/lib/mysql2psql/config.rb @@ -1,15 +1,9 @@ require 'mysql2psql/config_base' class Mysql2psql - class Config < ConfigBase - def initialize(yaml) - super(yaml) - end - end - -end \ No newline at end of file +end diff --git a/lib/mysql2psql/config_base.rb b/lib/mysql2psql/config_base.rb index 8452054..6397e57 100644 --- a/lib/mysql2psql/config_base.rb +++ b/lib/mysql2psql/config_base.rb @@ -2,36 +2,34 @@ require 'mysql2psql/errors' class Mysql2psql - class ConfigBase - attr_reader :config, :filepath + attr_reader :config def initialize(yaml) - @filepath=nil - @config = yaml #YAML::load(File.read(filepath)) + @config = yaml end - + def [](key) - self.send( key ) + send(key) end + def method_missing(name, *args) - token=name.to_s - default = args.length>0 ? args[0] : '' + token = name.to_s + default = args.length > 0 ? args[0] : '' must_be_defined = default == :none case token when /mysql/i - key=token.sub( /^mysql/, '' ) - value=config["mysql"][key] + key = token.sub(/^mysql/, '') + value = config['mysql'][key] when /dest/i - key=token.sub( /^dest/, '' ) - value=config["destination"][key] + key = token.sub(/^dest/, '') + value = config['destination'][key] when /only_tables/i - value=config["tables"] + value = config['tables'] else - value=config[token] + value = config[token] end - value.nil? ? ( must_be_defined ? (raise Mysql2psql::UninitializedValueError.new("no value and no default for #{name}")) : default ) : value + value.nil? ? (must_be_defined ? (fail Mysql2psql::UninitializedValueError.new("no value and no default for #{name}")) : default) : value end end - -end \ No newline at end of file +end diff --git a/lib/mysql2psql/connection.rb b/lib/mysql2psql/connection.rb index 266affe..e458aa3 100644 --- a/lib/mysql2psql/connection.rb +++ b/lib/mysql2psql/connection.rb @@ -1,133 +1,183 @@ - class Mysql2psql - class Connection - attr_reader :conn, :adapter, :hostname, :login, :password, :database, :schema, :port, :environment, :jruby, :copy_manager, :stream, :is_copying - + def initialize(options) - # Rails-centric stuffs + @environment = ENV['RAILS_ENV'].nil? ? 'development' : ENV['RAILS_ENV'] - if options.has_key?('config') and options['config'].has_key?('destination') and options['config']['destination'].has_key?(environment) - - pg_options = Config.new(YAML::load(options['config']['destination'][environment].to_yaml)) - @hostname, @login, @password, @database, @port = pg_options.hostname('localhost'), pg_options.username, pg_options.password, pg_options.database, pg_options.port(5432).to_s - @database, @schema = database.split(":") - - @adapter = pg_options.adapter("jdbcpostgresql") - + if options.key?('config') && options['config'].key?('destination') && options['config']['destination'].key?(environment) + + pg_options = Config.new(YAML.load(options['config']['destination'][environment].to_yaml)) + @hostname, @login, @password, @database, @port = pg_options.host('localhost'), pg_options.username, pg_options.password, pg_options.database, pg_options.port(5432).to_s + @database, @schema = database.split(':') + + @adapter = pg_options.adapter('jdbcpostgresql') + else - raise "Unable to locate PostgreSQL destination environment in the configuration file" + fail 'Unable to locate PostgreSQL destination environment in the configuration file' end - + if RUBY_PLATFORM == 'java' @jruby = true - + ActiveRecord::Base.establish_connection( - :adapter => adapter, - :database => database, - :username => login, - :password => password, - :host => hostname, - :port => port) - + adapter: adapter, + database: database, + username: login, + password: password, + host: hostname, + port: port) + @conn = ActiveRecord::Base.connection_pool.checkout - + unless conn.nil? raw_connection = conn.raw_connection @copy_manager = org.postgresql.copy.CopyManager.new(raw_connection.connection) else raise_nil_connection end - + else @jruby = false - - @conn = PG.connect( dbname: database, user: login, password: password, host: hostname, port: port ) - - unless conn.nil? - - else + + @conn = PG::Connection.open(dbname: database, user: login, password: password, host: hostname, port: port) + + if conn.nil? raise_nil_connection end - + end - + @is_copying = false - + @current_statement = '' end - + + # ensure that the copy is completed, in case we hadn't seen a '\.' in the data stream. + def flush + if jruby + stream.end_copy if @is_copying + else + conn.put_copy_end + end + rescue => e + $stderr.puts e + ensure + @is_copying = false + end + def execute(sql) - - if sql.match(/^COPY /) and ! is_copying - sql.chomp! # cHomp! cHomp! - + if sql.match(/^COPY /) && !is_copying + # sql.chomp! # cHomp! cHomp! + if jruby @stream = copy_manager.copy_in(sql) else - conn.exec( sql ) + conn.exec(sql) end - + @is_copying = true - - elsif sql.match(/^TRUNCATE /) and ! is_copying - - $stderr.puts "===> ERR: TRUNCATE is not implemented!" - - elsif sql.match(/^ALTER /) and ! is_copying - - $stderr.puts "===> ERR: ALTER is not implemented!" - - elsif is_copying - - if sql.match(/^\\\./) - - @is_copying = false - - if jruby - stream.end_copy - else - conn.put_copy_end - end - - else - - if jruby - begin - row = sql.to_java_bytes - stream.write_to_copy(row, 0, row.length) - rescue Exception => e - stream.cancel_copy - raise e - end + + elsif sql.match(/^(ALTER|CREATE|DROP|SELECT|SET|TRUNCATE) /) && !is_copying + + @current_statement = sql + + else + + if is_copying + + if sql.chomp == '\.' || sql.chomp.match(/^$/) + + flush + else - - begin - - until conn.put_copy_data( sql ) - $stderr.puts " waiting for connection to be writable..." - sleep 0.1 + + if jruby + + begin + row = sql.to_java_bytes + stream.write_to_copy(row, 0, row.length) + + rescue => e + + stream.cancel_copy + @is_copying = false + $stderr.puts e + + raise e + end + + else + + begin + + until conn.put_copy_data(sql) + $stderr.puts ' waiting for connection to be writable...' + sleep 0.1 + end + + rescue => e + @is_copying = false + $stderr.puts e + raise e end - - rescue Exception => e - $stderr.puts e - raise e end - end - + elsif @current_statement.length > 0 + @current_statement << ' ' + @current_statement << sql + else + # maybe a comment line? + end + end + + if @current_statement.match(/;$/) + run_statement(@current_statement) + @current_statement = '' + end + end + + # we're done talking to the database, so close the connection cleanly. + def finish + if jruby + ActiveRecord::Base.connection_pool.checkin(@conn) if @conn + else + @conn.finish if @conn + end + end + + # given a file containing psql syntax at path, pipe it down to the database. + def load_file(path) + if @conn + File.open(path, 'r:UTF-8') do |file| + file.each_line do |line| + execute(line) + end + flush end - + finish + else + raise_nil_connection + end + end + + def clear_schema + statements = ['DROP SCHEMA PUBLIC CASCADE', 'CREATE SCHEMA PUBLIC'] + statements.each do |statement| + run_statement(statement) end - end - + def raise_nil_connection - raise "No Connection" + fail 'No Connection' + end + + private + + def run_statement(statement) + method = jruby ? :execute : :exec + @conn.send(method, statement) end - end - -end \ No newline at end of file +end diff --git a/lib/mysql2psql/converter.rb b/lib/mysql2psql/converter.rb index 9eee278..4f9312b 100644 --- a/lib/mysql2psql/converter.rb +++ b/lib/mysql2psql/converter.rb @@ -1,9 +1,8 @@ class Mysql2psql - class Converter attr_reader :reader, :writer, :options - attr_reader :exclude_tables, :only_tables, :suppress_data, :suppress_ddl, :force_truncate, :preserve_order - + attr_reader :exclude_tables, :only_tables, :suppress_data, :suppress_ddl, :force_truncate, :preserve_order, :clear_schema + def initialize(reader, writer, options) @reader = reader @writer = writer @@ -14,63 +13,69 @@ def initialize(reader, writer, options) @suppress_ddl = options.suppress_ddl(false) @force_truncate = options.force_truncate(false) @preserve_order = options.preserve_order(false) + @clear_schema = options.clear_schema(false) end - + def convert - - tables = reader.tables. - reject {|table| @exclude_tables.include?(table.name)}. - select {|table| @only_tables ? @only_tables.include?(table.name) : true} - + tables = reader.tables + .reject { |table| @exclude_tables.include?(table.name) } + .select { |table| @only_tables ? @only_tables.include?(table.name) : true } + if @preserve_order reordered_tables = [] @only_tables.each do |only_table| - idx = tables.index {|table| table.name == only_table} + idx = tables.index { |table| table.name == only_table } reordered_tables << tables[idx] end tables = reordered_tables - + end tables.each do |table| writer.write_table(table) end unless @suppress_ddl - + # tables.each do |table| # writer.truncate(table) if force_truncate && suppress_ddl # writer.write_contents(table, reader) # end unless @suppress_data - + unless @suppress_data - + tables.each do |table| - writer.truncate(table) if force_truncate and suppress_ddl + writer.truncate(table) if force_truncate && suppress_ddl end - + tables.each do |table| writer.write_contents(table, reader) end - - writer.inload - + end - + tables.each do |table| writer.write_indexes(table) end unless @suppress_ddl tables.each do |table| writer.write_constraints(table) end unless @suppress_ddl - + writer.close - return 0 - + if @clear_schema + writer.clear_schema + end + + writer.inload + + 0 + rescue => e + $stderr.puts "Mysql2psql: Conversion failed: #{e.to_s}" + $stderr.puts e + $stderr.puts e.backtrace[0,3].join("\n") + return -1 end - end - end diff --git a/lib/mysql2psql/errors.rb b/lib/mysql2psql/errors.rb index 6aa63e5..0c4b969 100644 --- a/lib/mysql2psql/errors.rb +++ b/lib/mysql2psql/errors.rb @@ -1,16 +1,16 @@ - class Mysql2psql - class GeneralError < StandardError - end + end class ConfigurationError < StandardError - end + end + class UninitializedValueError < ConfigurationError - end + end + class ConfigurationFileNotFound < ConfigurationError - end + end + class ConfigurationFileInitialized < ConfigurationError - end - -end \ No newline at end of file + end +end diff --git a/lib/mysql2psql/mysql_reader.rb b/lib/mysql2psql/mysql_reader.rb index c9491d2..b6aa038 100644 --- a/lib/mysql2psql/mysql_reader.rb +++ b/lib/mysql2psql/mysql_reader.rb @@ -1,120 +1,117 @@ -require "rubygems" -require "bundler/setup" +require 'rubygems' +require 'bundler/setup' require 'mysql-pr' require 'csv' class Mysql2psql - class MysqlReader class Field end - + class Table attr_reader :name - + def initialize(reader, name) @reader = reader @name = name - end - - @@types = %w(tiny enum decimal short long float double null timestamp longlong int24 date time datetime year set blob string var_string char).inject({}) do |list, type| + + @@types = %w(tiny enum decimal short long float double null timestamp longlong int24 date time datetime year set blob string var_string char).reduce({}) do |list, type| list[eval("::MysqlPR::Field::TYPE_#{type.upcase}")] = type list end - - @@types[246] = "decimal" - + @@types[246] = 'decimal' + def columns @columns ||= load_columns end - + def convert_type(type) case type when /int.* unsigned/ - "bigint" + 'bigint' when /bigint/ - "bigint" - when "bit(1)" - "boolean" - when "tinyint(1)" - "boolean" + 'bigint' + when 'bit(1)' + 'boolean' + when 'tinyint(1)' + 'boolean' when /tinyint/ - "tinyint" + 'tinyint' when /int/ - "integer" + 'integer' when /varchar/ - "varchar" + 'varchar' when /char/ - "char" + 'char' when /decimal/ - "decimal" + 'decimal' when /(float|double)/ - "double precision" + 'double precision' + when /set/ + 'varchar' else type - end + end end - + def load_columns @reader.reconnect result = @reader.mysql.list_fields(name) - mysql_flags = ::MysqlPR::Field.constants.select {|c| c =~ /FLAG/} + mysql_flags = ::MysqlPR::Field.constants.select { |c| c =~ /FLAG/ } fields = [] - @reader.mysql.query("EXPLAIN `#{name}`") do |res| - while field = res.fetch_row do + @reader.query("EXPLAIN `#{name}`") do |res| + while field = res.fetch_row length = field[1][/\((\d+)\)/, 1] if field[1] =~ /\((\d+)\)/ length = field[1][/\((\d+),(\d+)\)/, 1] if field[1] =~ /\((\d+),(\d+)\)/ desc = { - :name => field[0], - :table_name => name, - :type => convert_type(field[1]), - :length => length && length.to_i, - :decimals => field[1][/\((\d+),(\d+)\)/, 2], - :null => field[2] == "YES", - :primary_key => field[3] == "PRI", - :auto_increment => field[5] == "auto_increment" - } + name: field[0], + table_name: name, + type: convert_type(field[1]), + length: length && length.to_i, + decimals: field[1][/\((\d+),(\d+)\)/, 2], + null: field[2] == 'YES', + primary_key: field[3] == 'PRI', + auto_increment: field[5] == 'auto_increment' + } desc[:default] = field[4] unless field[4].nil? fields << desc end end - - fields.select {|field| field[:auto_increment]}.each do |field| - @reader.mysql.query("SELECT max(`#{field[:name]}`) FROM `#{name}`") do |res| + + fields.select { |field| field[:auto_increment] }.each do |field| + @reader.query("SELECT max(`#{field[:name]}`) FROM `#{name}`") do |res| field[:maxval] = res.fetch_row[0].to_i end end fields end - - def indexes load_indexes unless @indexes - @indexes + @indexes end - + def foreign_keys load_indexes unless @foreign_keys @foreign_keys end - + def load_indexes @indexes = [] @foreign_keys = [] - - @reader.mysql.query("SHOW CREATE TABLE `#{name}`") do |result| + + @reader.query("SHOW CREATE TABLE `#{name}`") do |result| explain = result.fetch_row[1] explain.split(/\n/).each do |line| next unless line =~ / KEY / index = {} if match_data = /CONSTRAINT `(\w+)` FOREIGN KEY \((.*?)\) REFERENCES `(\w+)` \((.*?)\)(.*)/.match(line) - index[:name] = "fk_"+name+"_"+match_data[1] - index[:column] = match_data[2].gsub!('`','').split(', ') + index[:name] = 'fk_' + name + '_' + match_data[1] + index[:column] = match_data[2].gsub!('`', '').split(', ') index[:ref_table] = match_data[3] - index[:ref_column] = match_data[4].gsub!('`','').split(', ') - + index[:ref_column] = match_data[4].gsub!('`', '').split(', ') + the_rest = match_data[5] if match_data = /ON DELETE (SET NULL|SET DEFAULT|RESTRICT|NO ACTION|CASCADE)/.match(the_rest) @@ -128,80 +125,103 @@ def load_indexes else index[:on_update] ||= 'RESTRICT' end - + @foreign_keys << index elsif match_data = /KEY `(\w+)` \((.*)\)/.match(line) - index[:name] = "idx_"+name+"_"+match_data[1] - index[:columns] = match_data[2].split(",").map {|col| col[/`(\w+)`/, 1]} + index[:name] = 'idx_' + name + '_' + match_data[1] + index[:columns] = match_data[2].split(',').map { |col| col[/`(\w+)`/, 1] } index[:unique] = true if line =~ /UNIQUE/ @indexes << index elsif match_data = /PRIMARY KEY .*\((.*)\)/.match(line) index[:primary] = true - index[:columns] = match_data[1].split(",").map {|col| col.strip.gsub(/`/, "")} + index[:columns] = match_data[1].split(',').map { |col| col.strip.gsub(/`/, '') } @indexes << index end end end end - + def count_rows - @reader.mysql.query("SELECT COUNT(*) FROM `#{name}`") do |res| + @reader.query("SELECT COUNT(*) FROM `#{name}`") do |res| return res.fetch_row[0].to_i end end - + def has_id? - !!columns.find {|col| col[:name] == "id"} + !!columns.find { |col| col[:name] == 'id' } end - + def count_for_pager query = has_id? ? 'MAX(id)' : 'COUNT(*)' - @reader.mysql.query("SELECT #{query} FROM `#{name}`") do |res| + @reader.query("SELECT #{query} FROM `#{name}`") do |res| return res.fetch_row[0].to_i end end - + def query_for_pager query = has_id? ? 'WHERE id >= ? AND id < ?' : 'LIMIT ?,?' - "SELECT #{columns.map{|c| "`"+c[:name]+"`"}.join(", ")} FROM `#{name}` #{query}" + + cols = columns.map do |c| + if "multipolygon" == c[:type] + "AsWKT(`#{c[:name]}`) as `#{c[:name]}`" + else + "`#{c[:name]}`" + end + end + + "SELECT #{cols.join(", ")} FROM `#{name}` #{query}" end end - + def connect - @mysql = ::MysqlPR.connect(@host, @user, @passwd, @db, @port) - @mysql.query("SET NAMES utf8") - @mysql.query("SET SESSION query_cache_type = OFF") + @mysql = ::MysqlPR.connect(@host, @user, @passwd, @db, @port, @sock) + @mysql.charset = ::MysqlPR::Charset.by_number 192 # utf8_unicode_ci :: http://rubydoc.info/gems/mysql-pr/MysqlPR/Charset + @mysql.query('SET NAMES utf8') + @mysql.query('SET SESSION query_cache_type = OFF') end - + def reconnect @mysql.close rescue false connect end - + + def query(*args, &block) + self.mysql.query(*args, &block) + rescue Mysql::Error => e + if e.message =~ /gone away/i + self.reconnect + retry + else + puts "MySQL Query failed '#{args.inspect}' #{e.inspect}" + puts e.backtrace[0,5].join("\n") + return [] + end + end + def initialize(options) - @host, @user, @passwd, @db, @port, @sock, @flag = - options.mysqlhostname('localhost'), options.mysqlusername, - options.mysqlpassword, options.mysqldatabase, - options.mysqlport, options.mysqlsocket - @port = 3306 if @port == "" # for things like Amazon's RDS you don't have a port and connect fails with "" for a value - @sock = nil if @sock == "" - @flag = nil if @flag == "" + @host, @user, @passwd, @db, @port, @sock, @flag = + options.mysqlhost('localhost'), options.mysqlusername, + options.mysqlpassword, options.mysqldatabase, + options.mysqlport(3306), options.mysqlsocket, + options.mysqlflag + @sock = nil if @sock == '' + @flag = nil if @flag == '' connect end - + attr_reader :mysql - + def tables - @tables ||= @mysql.list_tables.map {|table| Table.new(self, table)} + @tables ||= @mysql.list_tables.map { |table| Table.new(self, table) } end - + def paginated_read(table, page_size) count = table.count_for_pager return if count < 1 statement = @mysql.prepare(table.query_for_pager) counter = 0 - 0.upto((count + page_size)/page_size) do |i| - statement.execute(i*page_size, table.has_id? ? (i+1)*page_size : page_size) + 0.upto((count + page_size) / page_size) do |i| + statement.execute(i * page_size, table.has_id? ? (i + 1) * page_size : page_size) while row = statement.fetch counter += 1 yield(row, counter) @@ -210,5 +230,4 @@ def paginated_read(table, page_size) counter end end - end \ No newline at end of file diff --git a/lib/mysql2psql/postgres_db_writer.rb b/lib/mysql2psql/postgres_db_writer.rb index 3523d3a..6476d4e 100644 --- a/lib/mysql2psql/postgres_db_writer.rb +++ b/lib/mysql2psql/postgres_db_writer.rb @@ -2,35 +2,22 @@ require 'mysql2psql/connection' class Mysql2psql - class PostgresDbWriter < PostgresFileWriter - attr_reader :connection, :filename - - def initialize(filename, options) + def initialize(filename, options) + # note that the superclass opens and truncates filename for writing super(filename) - @filename = filename - @connection = Connection.new(options) - end - - def inload - - File.open(filename, 'r') do |file| - - file.each_line do |line| - - connection.execute(line) - - end - - end - + + def inload(path = filename) + connection.load_file(path) end + def clear_schema + connection.clear_schema + end end - -end +end \ No newline at end of file diff --git a/lib/mysql2psql/postgres_file_writer.rb b/lib/mysql2psql/postgres_file_writer.rb index 4362e5c..4526ab1 100644 --- a/lib/mysql2psql/postgres_file_writer.rb +++ b/lib/mysql2psql/postgres_file_writer.rb @@ -1,142 +1,138 @@ require 'mysql2psql/postgres_writer' class Mysql2psql - -class PostgresFileWriter < PostgresWriter - def initialize(file) - @f = File.open(file, "w+") - @f << <<-EOF + class PostgresFileWriter < PostgresWriter + def initialize(file) + @f = File.open(file, 'w+:UTF-8') + @f << <<-EOF -- MySQL 2 PostgreSQL dump\n SET client_encoding = 'UTF8'; SET standard_conforming_strings = off; SET check_function_bodies = false; SET client_min_messages = warning; - + EOF - end - - def truncate(table) - serial_key = nil - maxval = nil - - table.columns.map do |column| - if column[:auto_increment] - serial_key = column[:name] - maxval = column[:maxval].to_i < 1 ? 1 : column[:maxval] + 1 - end end - @f << <<-EOF + def truncate(table) + serial_key = nil + maxval = nil + + table.columns.map do |column| + if column[:auto_increment] + serial_key = column[:name] + maxval = column[:maxval].to_i < 1 ? 1 : column[:maxval] + 1 + end + end + + @f << <<-EOF -- TRUNCATE #{table.name}; TRUNCATE #{PGconn.quote_ident(table.name)} CASCADE; EOF - if serial_key - @f << <<-EOF + if serial_key + @f << <<-EOF SELECT pg_catalog.setval(pg_get_serial_sequence('#{table.name}', '#{serial_key}'), #{maxval}, true); EOF - end - end - - def write_table(table) - primary_keys = [] - serial_key = nil - maxval = nil - - columns = table.columns.map do |column| - if column[:auto_increment] - serial_key = column[:name] - maxval = column[:maxval].to_i < 1 ? 1 : column[:maxval] + 1 end - if column[:primary_key] - primary_keys << column[:name] - end - " " + column_description(column) - end.join(",\n") - - if serial_key - - @f << <<-EOF + end + + def write_table(table) + primary_keys = [] + serial_key = nil + maxval = nil + + columns = table.columns.map do |column| + if column[:auto_increment] + serial_key = column[:name] + maxval = column[:maxval].to_i < 1 ? 1 : column[:maxval] + 1 + end + if column[:primary_key] + primary_keys << column[:name] + end + ' ' + column_description(column) + end.join(",\n") + + if serial_key + + @f << <<-EOF -- -- Name: #{table.name}_#{serial_key}_seq; Type: SEQUENCE; Schema: public -- - + DROP SEQUENCE IF EXISTS #{table.name}_#{serial_key}_seq CASCADE; - + CREATE SEQUENCE #{table.name}_#{serial_key}_seq INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1; - - + + SELECT pg_catalog.setval('#{table.name}_#{serial_key}_seq', #{maxval}, true); - + EOF - end - - @f << <<-EOF + end + + @f << <<-EOF -- Table: #{table.name} - + -- DROP TABLE #{table.name}; DROP TABLE IF EXISTS #{PGconn.quote_ident(table.name)} CASCADE; - + CREATE TABLE #{PGconn.quote_ident(table.name)} ( EOF - - @f << columns - - if primary_index = table.indexes.find {|index| index[:primary]} - @f << ",\n CONSTRAINT #{table.name}_pkey PRIMARY KEY(#{primary_index[:columns].map {|col| PGconn.quote_ident(col)}.join(", ")})" - end - - @f << <<-EOF + + @f << columns + + if primary_index = table.indexes.find { |index| index[:primary] } + @f << ",\n CONSTRAINT #{table.name}_pkey PRIMARY KEY(#{primary_index[:columns].map { |col| PGconn.quote_ident(col) }.join(', ')})" + end + + @f << <<-EOF \n) WITHOUT OIDS; EOF - - table.indexes.each do |index| - next if index[:primary] - unique = index[:unique] ? "UNIQUE " : nil - @f << <<-EOF + + table.indexes.each do |index| + next if index[:primary] + unique = index[:unique] ? 'UNIQUE ' : nil + @f << <<-EOF DROP INDEX IF EXISTS #{PGconn.quote_ident(index[:name])} CASCADE; -CREATE #{unique}INDEX #{PGconn.quote_ident(index[:name])} ON #{PGconn.quote_ident(table.name)} (#{index[:columns].map {|col| PGconn.quote_ident(col)}.join(", ")}); +CREATE #{unique}INDEX #{PGconn.quote_ident(index[:name])} ON #{PGconn.quote_ident(table.name)} (#{index[:columns].map { |col| PGconn.quote_ident(col) }.join(', ')}); EOF + end end - - end - - def write_indexes(table) - end - - def write_constraints(table) - table.foreign_keys.each do |key| - @f << "ALTER TABLE #{PGconn.quote_ident(table.name)} ADD FOREIGN KEY (#{key[:column].map{|c|PGconn.quote_ident(c)}.join(', ')}) REFERENCES #{PGconn.quote_ident(key[:ref_table])}(#{key[:ref_column].map{|c|PGconn.quote_ident(c)}.join(', ')}) ON UPDATE #{key[:on_update]} ON DELETE #{key[:on_delete]};\n" + + def write_indexes(_table) end - end - - - def write_contents(table, reader) - @f << <<-EOF + + def write_constraints(table) + table.foreign_keys.each do |key| + @f << "ALTER TABLE #{PGconn.quote_ident(table.name)} ADD FOREIGN KEY (#{key[:column].map { |c|PGconn.quote_ident(c) }.join(', ')}) REFERENCES #{PGconn.quote_ident(key[:ref_table])}(#{key[:ref_column].map { |c|PGconn.quote_ident(c) }.join(', ')}) ON UPDATE #{key[:on_update]} ON DELETE #{key[:on_delete]};\n" + end + end + + def write_contents(table, reader) + @f << <<-EOF -- -- Data for Name: #{table.name}; Type: TABLE DATA; Schema: public -- -COPY "#{table.name}" (#{table.columns.map {|column| PGconn.quote_ident(column[:name])}.join(", ")}) FROM stdin; +COPY "#{table.name}" (#{table.columns.map { |column| PGconn.quote_ident(column[:name]) }.join(', ')}) FROM stdin; EOF - - reader.paginated_read(table, 1000) do |row, counter| - line = [] - process_row(table, row) - @f << row.join("\t") + "\n" + + reader.paginated_read(table, 1000) do |row, _counter| + line = [] + process_row(table, row) + @f << row.join("\t") + "\n" + end + @f << "\\.\n\n" + # @f << "VACUUM FULL ANALYZE #{PGconn.quote_ident(table.name)};\n\n" + end + + def close + @f.close end - @f << "\\.\n\n" - #@f << "VACUUM FULL ANALYZE #{PGconn.quote_ident(table.name)};\n\n" - end - - def close - @f.close end end - -end \ No newline at end of file diff --git a/lib/mysql2psql/postgres_writer.rb b/lib/mysql2psql/postgres_writer.rb index c2b93fd..ffc9a45 100644 --- a/lib/mysql2psql/postgres_writer.rb +++ b/lib/mysql2psql/postgres_writer.rb @@ -1,141 +1,142 @@ require 'mysql2psql/writer' class Mysql2psql - class PostgresWriter < Writer def column_description(column) "#{PGconn.quote_ident(column[:name])} #{column_type_info(column)}" end - + def column_type(column) - column_type_info(column).split(" ").first + column_type_info(column).split(' ').first end - + def column_type_info(column) if column[:auto_increment] return "integer DEFAULT nextval('#{column[:table_name]}_#{column[:name]}_seq'::regclass) NOT NULL" end - - default = column[:default] ? " DEFAULT #{column[:default] == nil ? 'NULL' : "'"+PGconn.escape(column[:default])+"'"}" : nil - null = column[:null] ? "" : " NOT NULL" - type = + + default = column[:default] ? " DEFAULT #{column[:default].nil? ? 'NULL' : "'" + PGconn.escape(column[:default]) + "'"}" : nil + null = column[:null] ? '' : ' NOT NULL' + type = case column[:type] - + # String types - when "char" - default = default + "::char" if default + when 'char' + default = default + '::char' if default "character(#{column[:length]})" - when "varchar" - default = default + "::character varying" if default - # puts "VARCHAR: #{column.inspect}" + when 'varchar' + default = default + '::character varying' if default + # puts "VARCHAR: #{column.inspect}" "character varying(#{column[:length]})" - + # Integer and numeric types - when "integer" + when 'integer' default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_i}" if default - "integer" - when "bigint" + 'integer' + when 'bigint' default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_i}" if default - "bigint" - when "tinyint" + 'bigint' + when 'tinyint' default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_i}" if default - "smallint" - - when "boolean" + 'smallint' + + when 'boolean' default = " DEFAULT #{column[:default].to_i == 1 ? 'true' : 'false'}" if default - "boolean" - when "float" + 'boolean' + when 'float' default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_f}" if default - "real" - when "float unsigned" + 'real' + when 'float unsigned' default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default].to_f}" if default - "real" - when "decimal" + 'real' + when 'decimal' default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default]}" if default "numeric(#{column[:length] || 10}, #{column[:decimals] || 0})" - when "double precision" + when 'double precision' default = " DEFAULT #{column[:default].nil? ? 'NULL' : column[:default]}" if default - "double precision" + 'double precision' # Mysql datetime fields - when "datetime" + when 'datetime' default = nil - "timestamp without time zone" - when "date" + 'timestamp without time zone' + when 'date' default = nil - "date" - when "timestamp" - default = " DEFAULT CURRENT_TIMESTAMP" if column[:default] == "CURRENT_TIMESTAMP" - default = " DEFAULT '1970-01-01 00:00'" if column[:default] == "0000-00-00 00:00" - default = " DEFAULT '1970-01-01 00:00:00'" if column[:default] == "0000-00-00 00:00:00" - "timestamp without time zone" - when "time" - default = " DEFAULT NOW()" if default - "time without time zone" - - when "tinyblob" - "bytea" - when "mediumblob" - "bytea" - when "longblob" - "bytea" - when "blob" - "bytea" - when "varbinary" - "bytea" - when "tinytext" - "text" - when "mediumtext" - "text" - when "longtext" - "text" - when "text" - "text" + 'date' + when 'timestamp' + default = ' DEFAULT CURRENT_TIMESTAMP' if column[:default] == 'CURRENT_TIMESTAMP' + default = " DEFAULT '1970-01-01 00:00'" if column[:default] == '0000-00-00 00:00' + default = " DEFAULT '1970-01-01 00:00:00'" if column[:default] == '0000-00-00 00:00:00' + 'timestamp without time zone' + when 'time' + default = ' DEFAULT NOW()' if default + 'time without time zone' + + when 'tinyblob' + 'bytea' + when 'mediumblob' + 'bytea' + when 'longblob' + 'bytea' + when 'blob' + 'bytea' + when 'varbinary' + 'bytea' + when 'tinytext' + 'text' + when 'mediumtext' + 'text' + when 'longtext' + 'text' + when 'text' + 'text' when /^enum/ - default = default + "::character varying" if default + default = default + '::character varying' if default enum = column[:type].gsub(/enum|\(|\)/, '') - max_enum_size = enum.split(',').map{ |check| check.size() -2}.sort[-1] - "character varying(#{max_enum_size}) check( #{column[:name]} in (#{enum}))" + max_enum_size = enum.split(',').map { |check| check.size - 2 }.sort[-1] + "character varying(#{max_enum_size}) check( \"#{column[:name]}\" in (#{enum}))" + when 'multipolygon' + 'geometry' + when 'geometry' + 'geometry' else puts "Unknown #{column.inspect}" column[:type].inspect - return "" + return '' end "#{type}#{default}#{null}" end - + def process_row(table, row) - table.columns.each_with_index do |column, index| + table.columns.each_with_index do |column, index| - if column[:type] == "time" - row[index] = "%02d:%02d:%02d" % [row[index].hour, row[index].minute, row[index].second] - end - - if row[index].is_a?(MysqlPR::Time) - row[index] = row[index].to_s.gsub('0000-00-00 00:00', '1970-01-01 00:00') - row[index] = row[index].to_s.gsub('0000-00-00 00:00:00', '1970-01-01 00:00:00') - end - - if column_type(column) == "boolean" - row[index] = row[index] == 1 ? 't' : row[index] == 0 ? 'f' : row[index] - end - - if row[index].is_a?(String) - if column_type(column) == "bytea" - row[index] = PGconn.escape_bytea(row[index]) - else - row[index] = row[index].gsub(/\\/, '\\\\\\').gsub(/\n/,'\n').gsub(/\t/,'\t').gsub(/\r/,'\r').gsub(/\0/, '') - end + if column[:type] == 'time' + row[index] = '%02d:%02d:%02d' % [row[index].hour, row[index].minute, row[index].second] unless row[index].nil? + end + + if row[index].is_a?(MysqlPR::Time) + row[index] = row[index].to_s.gsub('0000-00-00 00:00', '1970-01-01 00:00') + row[index] = row[index].to_s.gsub('0000-00-00 00:00:00', '1970-01-01 00:00:00') + end + + if column_type(column) == 'boolean' + row[index] = row[index] == 1 ? 't' : row[index] == 0 ? 'f' : row[index] + end + + if row[index].is_a?(String) + if column_type(column) == 'bytea' + row[index] = PGconn.escape_bytea(row[index]) + else + row[index] = row[index].gsub(/\\/, '\\\\\\').gsub(/\n/, '\n').gsub(/\t/, '\t').gsub(/\r/, '\r').gsub(/\0/, '') end - - row[index] = '\N' if !row[index] end + + row[index] = '\N' unless row[index] + end end - - def truncate(table) + + def truncate(_table) end - end - -end \ No newline at end of file +end diff --git a/lib/mysql2psql/version.rb b/lib/mysql2psql/version.rb index c61d5c1..db7898c 100644 --- a/lib/mysql2psql/version.rb +++ b/lib/mysql2psql/version.rb @@ -1,9 +1,9 @@ class Mysql2psql module Version MAJOR = 0 - MINOR = 1 - PATCH = 0 + MINOR = 3 + PATCH = 1 STRING = [MAJOR, MINOR, PATCH].compact.join('.') end -end \ No newline at end of file +end diff --git a/lib/mysql2psql/writer.rb b/lib/mysql2psql/writer.rb index 549fe04..5389139 100644 --- a/lib/mysql2psql/writer.rb +++ b/lib/mysql2psql/writer.rb @@ -1,11 +1,7 @@ class Mysql2psql - class Writer - def inload - raise "Method 'inload' needs to be overridden..." + fail "Method 'inload' needs to be overridden..." end - end - -end \ No newline at end of file +end diff --git a/lib/mysql-to-postgres.rb b/lib/mysqltopostgres.rb similarity index 65% rename from lib/mysql-to-postgres.rb rename to lib/mysqltopostgres.rb index ad78110..f261ecf 100644 --- a/lib/mysql-to-postgres.rb +++ b/lib/mysqltopostgres.rb @@ -1,14 +1,13 @@ - if RUBY_PLATFORM == 'java' require 'active_record' require 'postgres-pr/postgres-compat' else + require 'pg' require 'pg_ext' require 'pg/exceptions' require 'pg/constants' require 'pg/connection' require 'pg/result' - require 'pg' end require 'mysql2psql/errors' @@ -22,31 +21,36 @@ require 'mysql2psql/postgres_db_writer.rb' class Mysql2psql - attr_reader :options, :reader, :writer - + def initialize(yaml) - - @options = Config.new( yaml ) - + @options = Config.new(yaml) end - + + def send_file_to_postgres(path) + connection = Connection.new(options) + connection.load_file(path) + end + def convert - - @reader = MysqlReader.new( options ) - + @reader = MysqlReader.new(options) + tag = Time.new.to_s.gsub(/((\-)|( )|(:))+/, '') - - filename = File.expand_path('./' + tag + '_output.sql') + + path = './' + + unless options.config['dump_file_directory'].nil? + path = options.config['dump_file_directory'] + end + + filename = File.expand_path(File.join(path, tag + '_output.sql')) @writer = PostgresDbWriter.new(filename, options) Converter.new(reader, writer, options).convert - + if options.config['remove_dump_file'] - File.delete filename if File::exist?( filename ) + File.delete filename if File.exist?(filename) end - end - -end \ No newline at end of file +end diff --git a/mysql-to-postgres-0.2.12.gem b/mysql-to-postgres-0.2.12.gem deleted file mode 100644 index 6e3d5d4..0000000 Binary files a/mysql-to-postgres-0.2.12.gem and /dev/null differ diff --git a/mysql-to-postgres.gemspec b/mysql-to-postgres.gemspec deleted file mode 100644 index 32b651c..0000000 --- a/mysql-to-postgres.gemspec +++ /dev/null @@ -1,77 +0,0 @@ - -Gem::Specification.new do |s| - s.name = %q{mysql-to-postgres} - s.version = "0.2.12" - - s.authors = ["Max Lapshin ", "Anton Ageev ", "Samuel Tribehou ", "Marco Nenciarini ", "James Nobis ", "quel ", "Holger Amann ", "Maxim Dobriakov ", "Michael Kimsal ", "Jacob Coby ", "Neszt Tibor ", "Miroslav Kratochvil ", "Paul Gallagher ", "Alex C Jokela "] - s.date = %q{2010-09-19} - s.default_executable = %q{mysql-to-postgres} - s.description = %q{Translates MySQL -> PostgreSQL via pure ruby MySQL} - s.email = %q{ajokela@umn.edu} - s.executables = ["mysql-to-postgres"] - - s.files = [ - ".gitignore", - "MIT-LICENSE", - "README.md", - "Rakefile", - "bin/mysql-to-postgres", - "lib/mysql-to-postgres.rb", - "lib/mysql2psql/config.rb", - "lib/mysql2psql/config_base.rb", - "lib/mysql2psql/converter.rb", - "lib/mysql2psql/errors.rb", - "lib/mysql2psql/mysql_reader.rb", - "lib/mysql2psql/postgres_db_writer.rb", - "lib/mysql2psql/postgres_file_writer.rb", - "lib/mysql2psql/postgres_db_writer.rb", - "lib/mysql2psql/postgres_writer.rb", - "lib/mysql2psql/version.rb", - "lib/mysql2psql/writer.rb", - "mysql-to-postgres.gemspec", - "test/fixtures/config_all_options.yml", - "test/fixtures/seed_integration_tests.sql", - "test/integration/convert_to_db_test.rb", - "test/integration/convert_to_file_test.rb", - "test/integration/converter_test.rb", - "test/integration/mysql_reader_base_test.rb", - "test/integration/mysql_reader_test.rb", - "test/integration/postgres_db_writer_base_test.rb", - "test/lib/ext_test_unit.rb", - "test/lib/test_helper.rb", - "test/units/config_base_test.rb", - "test/units/config_test.rb", - "test/units/postgres_file_writer_test.rb" - ] - s.homepage = %q{https://github.com/ajokela/mysql-to-postgres} - s.rdoc_options = ["--charset=UTF-8"] - s.require_paths = ["lib"] - s.rubygems_version = %q{1.3.7} - s.summary = %q{Tool for converting mysql database to postgresql} - s.test_files = [ - "test/integration/convert_to_db_test.rb", - "test/integration/convert_to_file_test.rb", - "test/integration/converter_test.rb", - "test/integration/mysql_reader_base_test.rb", - "test/integration/mysql_reader_test.rb", - "test/integration/postgres_db_writer_base_test.rb", - "test/lib/ext_test_unit.rb", - "test/lib/test_helper.rb", - "test/units/config_base_test.rb", - "test/units/config_test.rb", - "test/units/postgres_file_writer_test.rb" - ] - - s.add_dependency(%q, [">= 2.9.10"]) - s.add_dependency(%q, ["= 0.6.3"]) - s.add_dependency(%q, [">= 3.2.6"]) - s.add_dependency(%q, [">= 2.1.1"]) - - if RUBY_PLATFORM == 'java' - s.add_dependency(%q, [">= 1.2.2"]) - s.add_dependency(%q, [">= 1.2.2"]) - s.add_dependency(%q, [">= 1.2.2"]) - end - -end - diff --git a/mysqltopostgres.gemspec b/mysqltopostgres.gemspec new file mode 100644 index 0000000..dedccbc --- /dev/null +++ b/mysqltopostgres.gemspec @@ -0,0 +1,95 @@ + +Gem::Specification.new do |s| + s.name = 'mysqltopostgres' + s.version = '0.3.1' + s.licenses = ['MIT'] + + s.authors = [ + 'Max Lapshin ', + 'Anton Ageev ', + 'Samuel Tribehou ', + 'Marco Nenciarini ', + 'James Nobis ', + 'quel ', + 'Holger Amann ', + 'Maxim Dobriakov ', + 'Michael Kimsal ', + 'Jacob Coby ', + 'Neszt Tibor ', + 'Miroslav Kratochvil ', + 'Paul Gallagher ', + 'Alex C Jokela ', + 'Peter Clark ', + 'Juga Paazmaya ' + ] + s.date = '2015-11-26' + s.default_executable = 'mysqltopostgres' + s.description = 'Translates MySQL -> PostgreSQL' + s.email = 'paazmaya@yahoo.com' + s.executables = ['mysqltopostgres'] + + s.files = [ + '.gitignore', + 'MIT-LICENSE', + 'README.md', + 'Rakefile', + 'bin/mysqltopostgres', + 'lib/mysqltopostgres.rb', + 'lib/mysql2psql/config.rb', + 'lib/mysql2psql/config_base.rb', + 'lib/mysql2psql/converter.rb', + 'lib/mysql2psql/connection.rb', + 'lib/mysql2psql/errors.rb', + 'lib/mysql2psql/mysql_reader.rb', + 'lib/mysql2psql/postgres_db_writer.rb', + 'lib/mysql2psql/postgres_file_writer.rb', + 'lib/mysql2psql/postgres_db_writer.rb', + 'lib/mysql2psql/postgres_writer.rb', + 'lib/mysql2psql/version.rb', + 'lib/mysql2psql/writer.rb', + 'mysqltopostgres.gemspec', + 'test/fixtures/config_all_options.yml', + 'test/fixtures/seed_integration_tests.sql', + 'test/integration/convert_to_db_test.rb', + 'test/integration/convert_to_file_test.rb', + 'test/integration/converter_test.rb', + 'test/integration/mysql_reader_base_test.rb', + 'test/integration/mysql_reader_test.rb', + 'test/integration/postgres_db_writer_base_test.rb', + 'test/lib/ext_test_unit.rb', + 'test/lib/test_helper.rb', + 'test/units/config_base_test.rb', + 'test/units/config_test.rb', + 'test/units/postgres_file_writer_test.rb' + ] + s.homepage = 'https://github.com/maxlapshin/mysql2postgres' + s.rdoc_options = ['--charset=UTF-8'] + s.require_paths = ['lib'] + s.rubygems_version = '2.4.0' + s.summary = 'MySQL to PostgreSQL Data Translation' + s.test_files = [ + 'test/integration/convert_to_db_test.rb', + 'test/integration/convert_to_file_test.rb', + 'test/integration/converter_test.rb', + 'test/integration/mysql_reader_base_test.rb', + 'test/integration/mysql_reader_test.rb', + 'test/integration/postgres_db_writer_base_test.rb', + 'test/lib/ext_test_unit.rb', + 'test/lib/test_helper.rb', + 'test/units/config_base_test.rb', + 'test/units/config_test.rb', + 'test/units/postgres_file_writer_test.rb' + ] + + s.add_dependency('mysql-pr', ['~> 2.9']) + s.add_dependency('postgres-pr', ['~> 0.6']) + s.add_dependency('test-unit', ['~> 2.1']) + + if RUBY_PLATFORM == 'java' + s.add_dependency('activerecord', ['~> 3.2']) + s.add_dependency('jdbc-postgres', ['~> 9.4']) + s.add_dependency('activerecord-jdbc-adapter', ['~> 1.2']) + s.add_dependency('activerecord-jdbcpostgresql-adapter', ['~> 1.2']) + end + +end diff --git a/test/integration/convert_to_db_test.rb b/test/integration/convert_to_db_test.rb index 249c45e..34c65fa 100644 --- a/test/integration/convert_to_db_test.rb +++ b/test/integration/convert_to_db_test.rb @@ -1,29 +1,28 @@ require 'test_helper' -require 'mysql2psql' +require 'mysqltopostgres' class ConvertToDbTest < Test::Unit::TestCase - class << self def startup seed_test_database - @@options=get_test_config_by_label(:localmysql_to_db_convert_all) - @@mysql2psql = Mysql2psql.new([@@options.filepath]) + @@options = get_test_config_by_label(:localmysql_to_db_convert_all) + @@mysql2psql = Mysql2psql.new(@@options) @@mysql2psql.convert @@mysql2psql.writer.open end + def shutdown @@mysql2psql.writer.close - delete_files_for_test_config(@@options) end end def setup end + def teardown end def test_table_creation assert_true @@mysql2psql.writer.exists?('numeric_types_basics') end - -end \ No newline at end of file +end diff --git a/test/integration/convert_to_file_test.rb b/test/integration/convert_to_file_test.rb index 073b3b6..4301923 100644 --- a/test/integration/convert_to_file_test.rb +++ b/test/integration/convert_to_file_test.rb @@ -1,66 +1,74 @@ require 'test_helper' -require 'mysql2psql' +require 'mysqltopostgres' class ConvertToFileTest < Test::Unit::TestCase - class << self def startup seed_test_database - @@options=get_test_config_by_label(:localmysql_to_file_convert_all) - @@mysql2psql = Mysql2psql.new([@@options.filepath]) + @@options = get_test_config_by_label(:localmysql_to_file_convert_all) + @@mysql2psql = Mysql2psql.new(@@options) @@mysql2psql.convert @@content = IO.read(@@mysql2psql.options.destfile) end - def shutdown - delete_files_for_test_config(@@options) - end + end def setup end + def teardown end + def content @@content end - + def test_table_creation assert_not_nil content.match('DROP TABLE IF EXISTS "numeric_types_basics" CASCADE') assert_not_nil content.match(/CREATE TABLE "numeric_types_basics"/) end def test_basic_numerics_tinyint - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_tinyint" smallint,.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_tinyint" smallint,.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_smallint - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_smallint" integer,.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_smallint" integer,.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_mediumint - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_mediumint" integer,.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_mediumint" integer,.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_int - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_int" integer,.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_int" integer,.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_integer - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_integer" integer,.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_integer" integer,.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_bigint - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_bigint" bigint,.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_bigint" bigint,.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_real - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_real" double precision,.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_real" double precision,.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_double - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_double" double precision,.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_double" double precision,.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_float - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_float" double precision,.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_float" double precision,.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_decimal - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_decimal" numeric\(10, 0\),.*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_decimal" numeric\(10, 0\),.*\)', Regexp::MULTILINE).match(content) end + def test_basic_numerics_numeric - assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_numeric" numeric\(10, 0\)[\w\n]*\)', Regexp::MULTILINE).match( content ) + assert_not_nil Regexp.new('CREATE TABLE "numeric_types_basics".*"f_numeric" numeric\(10, 0\)[\w\n]*\)', Regexp::MULTILINE).match(content) end - -end \ No newline at end of file +end diff --git a/test/integration/converter_test.rb b/test/integration/converter_test.rb index a362d9d..f44b79a 100644 --- a/test/integration/converter_test.rb +++ b/test/integration/converter_test.rb @@ -3,32 +3,29 @@ require 'mysql2psql/converter' class ConverterTest < Test::Unit::TestCase - class << self def startup seed_test_database - @@options=get_test_config_by_label(:localmysql_to_file_convert_nothing) - end - def shutdown - delete_files_for_test_config(@@options) + @@options = get_test_config_by_label(:localmysql_to_file_convert_nothing) end + end def setup end + def teardown end + def options @@options end - + def test_new_converter assert_nothing_raised do - reader=get_test_reader(options) - writer=get_test_file_writer(options) - converter=Mysql2psql::Converter.new(reader,writer,options) - assert_equal 0,converter.convert + reader = get_test_reader(options) + writer = get_test_file_writer(options) + converter = Mysql2psql::Converter.new(reader, writer, options) + assert_equal 0, converter.convert end end - - -end \ No newline at end of file +end diff --git a/test/integration/mysql_reader_base_test.rb b/test/integration/mysql_reader_base_test.rb index 2616d88..58c054c 100644 --- a/test/integration/mysql_reader_base_test.rb +++ b/test/integration/mysql_reader_base_test.rb @@ -3,40 +3,41 @@ require 'mysql2psql/mysql_reader' class MysqlReaderBaseTest < Test::Unit::TestCase - class << self def startup seed_test_database @@options = get_test_config_by_label(:localmysql_to_file_convert_nothing) end - def shutdown - delete_files_for_test_config(@@options) - end + end def setup end + def teardown end + def options @@options end - + def test_mysql_connection assert_nothing_raised do reader = Mysql2psql::MysqlReader.new(options) end end + def test_mysql_reconnect assert_nothing_raised do reader = Mysql2psql::MysqlReader.new(options) reader.reconnect end end + def test_mysql_connection_without_port assert_nothing_raised do - options.mysqlport = "" - options.mysqlsocket = "" + options.mysqlport = '' + options.mysqlsocket = '' reader = Mysql2psql::MysqlReader.new(options) end end -end \ No newline at end of file +end diff --git a/test/integration/mysql_reader_test.rb b/test/integration/mysql_reader_test.rb index 87c1be3..0b7adc5 100644 --- a/test/integration/mysql_reader_test.rb +++ b/test/integration/mysql_reader_test.rb @@ -1,47 +1,48 @@ require 'test_helper' class MysqlReaderTest < Test::Unit::TestCase - class << self def startup seed_test_database @@options = get_test_config_by_label(:localmysql_to_file_convert_nothing) - @@reader=get_test_reader(@@options) - end - def shutdown - delete_files_for_test_config(@@options) + @@reader = get_test_reader(@@options) end + end def setup end + def teardown end + def reader @@reader end - + def test_db_connection assert_nothing_raised do reader.mysql.ping end end + def test_tables_collection - values = reader.tables.select{|t| t.name == 'numeric_types_basics'} - assert_true values.length==1 + values = reader.tables.select { |t| t.name == 'numeric_types_basics' } + assert_true values.length == 1 assert_equal 'numeric_types_basics', values[0].name end + def test_paginated_read - expected_rows=3 - page_size=2 - expected_pages=(1.0 * expected_rows / page_size).ceil - - row_count=my_row_count=0 - table = reader.tables.select{|t| t.name == 'numeric_types_basics'}[0] - reader.paginated_read(table, page_size) do |row,counter| - row_count=counter - my_row_count+=1 + expected_rows = 3 + page_size = 2 + expected_pages = (1.0 * expected_rows / page_size).ceil + + row_count = my_row_count = 0 + table = reader.tables.select { |t| t.name == 'numeric_types_basics' }[0] + reader.paginated_read(table, page_size) do |_row, counter| + row_count = counter + my_row_count += 1 end assert_equal expected_rows, row_count assert_equal expected_rows, my_row_count end -end \ No newline at end of file +end diff --git a/test/integration/postgres_db_writer_base_test.rb b/test/integration/postgres_db_writer_base_test.rb index 8e25129..86e9235 100644 --- a/test/integration/postgres_db_writer_base_test.rb +++ b/test/integration/postgres_db_writer_base_test.rb @@ -3,28 +3,26 @@ require 'mysql2psql/postgres_db_writer' class PostgresDbWriterBaseTest < Test::Unit::TestCase - class << self def startup seed_test_database - @@options=get_test_config_by_label(:localmysql_to_db_convert_nothing) - end - def shutdown - delete_files_for_test_config(@@options) + @@options = get_test_config_by_label(:localmysql_to_db_convert_nothing) end + end def setup end + def teardown end + def options @@options end - + def test_pg_connection assert_nothing_raised do reader = Mysql2psql::PostgresDbWriter.new(options) end end - -end \ No newline at end of file +end diff --git a/test/lib/ext_test_unit.rb b/test/lib/ext_test_unit.rb index 025e35e..072f300 100644 --- a/test/lib/ext_test_unit.rb +++ b/test/lib/ext_test_unit.rb @@ -1,11 +1,9 @@ module Test::Unit - class TestCase - def self.must(name, &block) - test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym + test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym defined = instance_method(test_name) rescue false - raise "#{test_name} is already defined in #{self}" if defined + fail "#{test_name} is already defined in #{self}" if defined if block_given? define_method(test_name, &block) else @@ -14,17 +12,15 @@ def self.must(name, &block) end end end - end end - module Test::Unit::Assertions - def assert_false(object, message="") + def assert_false(object, message = '') assert_equal(false, object, message) end - def assert_true(object, message="") + + def assert_true(object, message = '') assert_equal(true, object, message) end end - diff --git a/test/lib/test_helper.rb b/test/lib/test_helper.rb index 0091d46..9437852 100644 --- a/test/lib/test_helper.rb +++ b/test/lib/test_helper.rb @@ -1,7 +1,7 @@ require 'rubygems' begin gem 'test-unit' - require "test/unit" + require 'test/unit' rescue LoadError # assume using stdlib Test:Unit require 'test/unit' @@ -10,22 +10,20 @@ require 'ext_test_unit' def seed_test_database - options=get_test_config_by_label(:localmysql_to_file_convert_nothing) + options = get_test_config_by_label(:localmysql_to_file_convert_nothing) seedfilepath = "#{File.dirname(__FILE__)}/../fixtures/seed_integration_tests.sql" - rc=system("mysql -u#{options.mysqlusername} #{options.mysqldatabase} < #{seedfilepath}") - raise StandardError unless rc + rc = system("mysql -u#{options.mysqlusername} #{options.mysqldatabase} < #{seedfilepath}") + fail StandardError unless rc return true rescue - raise StandardError.new("Failed to seed integration test db. See README for setup requirements.") -ensure - delete_files_for_test_config(options) + raise StandardError.new('Failed to seed integration test db. See README for setup requirements.') end def get_test_reader(options) require 'mysql2psql/mysql_reader' Mysql2psql::MysqlReader.new(options) rescue - raise StandardError.new("Failed to initialize integration test db. See README for setup requirements.") + raise StandardError.new('Failed to initialize integration test db. See README for setup requirements.') end def get_test_file_writer(options) @@ -38,9 +36,9 @@ def get_test_file_writer(options) def get_test_converter(options) require 'mysql2psql/converter' - reader=get_test_reader(options) - writer=get_test_file_writer(options) - Mysql2psql::Converter.new(reader,writer,options) + reader = get_test_reader(options) + writer = get_test_file_writer(options) + Mysql2psql::Converter.new(reader, writer, options) rescue raise StandardError.new("Failed to initialize converter from #{options.inspect}. See README for setup requirements.") end @@ -49,19 +47,19 @@ def get_temp_file(basename) require 'tempfile' f = Tempfile.new(basename) path = f.path - f.close!() + f.close! path end - def get_new_test_config(to_file = true, include_tables = [], exclude_tables = [], suppress_data = false, suppress_ddl = false, force_truncate = false) require 'mysql2psql/config' require 'mysql2psql/config_base' to_filename = to_file ? get_temp_file('mysql2psql_tmp_output') : nil configtext = Mysql2psql::Config.template(to_filename, include_tables, exclude_tables, suppress_data, suppress_ddl, force_truncate) - configfile=get_temp_file('mysql2psql_tmp_config') - File.open(configfile, 'w') {|f| f.write(configtext) } - Mysql2psql::ConfigBase.new( configfile ) + configfile = get_temp_file('mysql2psql_tmp_config') + File.open(configfile, 'w:UTF-8') { |f| f.write(configtext) } + yaml = YAML.load_file configfile + Mysql2psql::ConfigBase.new(yaml) rescue raise StandardError.new("Failed to initialize options from #{configfile}. See README for setup requirements.") end @@ -77,12 +75,7 @@ def get_test_config_by_label(name) when :localmysql_to_db_convert_nothing get_new_test_config(false, ['unobtainium'], ['kryptonite'], true, true, false) else - raise StandardError.new("Invalid label: #{name}") + fail StandardError.new("Invalid label: #{name}") end end -def delete_files_for_test_config(config) - File.delete(config.destfile) if File.exists?(config.destfile) - File.delete(config.filepath) if File.exists?(config.filepath) -rescue -end \ No newline at end of file diff --git a/test/units/config_base_test.rb b/test/units/config_base_test.rb index ca9434c..9498f2e 100644 --- a/test/units/config_base_test.rb +++ b/test/units/config_base_test.rb @@ -1,22 +1,22 @@ require 'test_helper' -require 'mysql2psql/config_base' +require 'mysqltopostgres' # # class ConfigBaseTest < Test::Unit::TestCase - attr_reader :config, :configfilepath + attr_reader :config, :yaml def setup - @configfilepath="#{File.dirname(__FILE__)}/../fixtures/config_all_options.yml" - @config = Mysql2psql::ConfigBase.new( configfilepath ) + @yaml = YAML.load_file "#{File.dirname(__FILE__)}/../fixtures/config_all_options.yml" + @config = Mysql2psql::ConfigBase.new(yaml) end - + def teardown @config = nil end - + def test_config_loaded assert_not_nil config.config - assert_equal configfilepath,config.filepath + assert_equal yaml, config.config end def test_uninitialized_error_when_not_found_and_no_default @@ -24,26 +24,25 @@ def test_uninitialized_error_when_not_found_and_no_default value = @config.not_found(:none) end end - + def test_default_when_not_found expected = 'defaultvalue' value = @config.not_found(expected) - assert_equal expected,value + assert_equal expected, value end - + def test_mysql_hostname value = @config.mysqlhostname - assert_equal 'localhost',value + assert_equal 'localhost', value end def test_mysql_hostname_array_access value = @config[:mysqlhostname] - assert_equal 'localhost',value + assert_equal 'localhost', value end - + def test_dest_file value = @config.destfile - assert_equal 'somefile',value + assert_equal 'somefile', value end - end diff --git a/test/units/config_test.rb b/test/units/config_test.rb index 5d531f6..85fc706 100644 --- a/test/units/config_test.rb +++ b/test/units/config_test.rb @@ -3,29 +3,14 @@ require 'mysql2psql/config' class ConfigTest < Test::Unit::TestCase - attr_reader :configfile_new, :configfile_all_opts, :configfile_not_found + attr_reader :config_all_opts def setup - @configfile_all_opts = "#{File.dirname(__FILE__)}/../fixtures/config_all_options.yml" - @configfile_not_found = "#{File.dirname(__FILE__)}/../fixtures/config_not_found.yml.do_not_create_this_file" - @configfile_new = get_temp_file('mysql2psql_test_config') - end - def teardown - File.delete(configfile_new) if File.exists?(configfile_new) + @config_all_opts = YAML.load_file "#{File.dirname(__FILE__)}/../fixtures/config_all_options.yml" end def test_config_loaded - value = Mysql2psql::Config.new(configfile_all_opts, false) + value = Mysql2psql::Config.new(config_all_opts) assert_not_nil value end - - def test_config_file_not_found - assert_raise(Mysql2psql::ConfigurationFileNotFound) do - value = Mysql2psql::Config.new(configfile_not_found, false) - end - end - def test_initialize_new_config_file - assert_raise(Mysql2psql::ConfigurationFileInitialized) do - value = Mysql2psql::Config.new(configfile_new, true) - end - end -end \ No newline at end of file + +end diff --git a/test/units/postgres_file_writer_test.rb b/test/units/postgres_file_writer_test.rb index a51107f..a2d4a7a 100644 --- a/test/units/postgres_file_writer_test.rb +++ b/test/units/postgres_file_writer_test.rb @@ -1,29 +1,24 @@ require 'test_helper' -require 'mysql2psql' +require 'mysqltopostgres' class PostgresFileWriterTest < Test::Unit::TestCase attr_accessor :destfile def setup - begin - f = Tempfile.new('mysql2psql_test_destfile') - @destfile = f.path - f.close!() - rescue => e - raise StandardError.new("Failed to initialize integration test db. See README for setup requirements.") - end + @destfile = get_temp_file('mysql2psql_test_destfile') + rescue => e + raise StandardError.new('Failed to initialize integration test db. See README for setup requirements.') end + def teardown - File.delete(destfile) if File.exists?(destfile) + File.delete(destfile) if File.exist?(destfile) end - + def test_basic_write writer = Mysql2psql::PostgresFileWriter.new(destfile) writer.close content = IO.read(destfile) assert_not_nil content.match("SET client_encoding = 'UTF8'") - assert_nil content.match("unobtanium") + assert_nil content.match('unobtanium') end - - -end \ No newline at end of file +end