From b6c72f8456a9d3c52e9c237655eec0f0a9f18ae8 Mon Sep 17 00:00:00 2001 From: Zach Dennis Date: Sun, 26 Sep 2010 16:03:48 -0400 Subject: [PATCH] Providing support for the Mysql2 adapter. --- Gemfile | 1 + Rakefile | 5 +- .../active_record/adapters/mysql2_adapter.rb | 6 + .../active_record/adapters/mysql_adapter.rb | 45 +------ .../active_record/adapters/mysql_base.rb | 48 +++++++ lib/activerecord-import/mysql2.rb | 2 + test/adapters/mysql2.rb | 1 + test/database.yml | 4 + test/mysql/import_test.rb | 116 +---------------- test/mysql2/import_test.rb | 7 ++ test/support/mysql/import_examples.rb | 117 ++++++++++++++++++ 11 files changed, 192 insertions(+), 160 deletions(-) create mode 100644 lib/activerecord-import/active_record/adapters/mysql2_adapter.rb create mode 100644 lib/activerecord-import/active_record/adapters/mysql_base.rb create mode 100644 lib/activerecord-import/mysql2.rb create mode 100644 test/adapters/mysql2.rb create mode 100644 test/mysql2/import_test.rb create mode 100644 test/support/mysql/import_examples.rb diff --git a/Gemfile b/Gemfile index 3cf4703..d25faef 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,7 @@ gem "rails", ">= 3.0.0.rc" group :test do # Database Adapters gem "mysql", ">= 2.8.1" + gem "mysql2", ">= 0.2.4" gem "pg", ">= 0.9.0" gem "sqlite3-ruby", ">= 1.3.1" diff --git a/Rakefile b/Rakefile index 4be6cb4..bca0ac0 100644 --- a/Rakefile +++ b/Rakefile @@ -31,7 +31,7 @@ namespace :display do end task :default => ["display:notice"] -ADAPTERS = %w(mysql postgresql sqlite3) +ADAPTERS = %w(mysql mysql2 postgresql sqlite3) ADAPTERS.each do |adapter| namespace :test do desc "Runs #{adapter} database tests." @@ -44,9 +44,10 @@ end begin require 'rcov/rcovtask' + adapter = ENV['ARE_DB'] Rcov::RcovTask.new do |test| test.libs << 'test' - test.pattern = "test/*_test.rb" + test.pattern = ["test/adapters/#{adapter}.rb", "test/*_test.rb", "test/#{adapter}/**/*_test.rb"] test.verbose = true end rescue LoadError diff --git a/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb b/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb new file mode 100644 index 0000000..4e6adb7 --- /dev/null +++ b/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb @@ -0,0 +1,6 @@ +require "active_record/connection_adapters/mysql2_adapter" +require "activerecord-import/active_record/adapters/mysql_base" + +class ActiveRecord::ConnectionAdapters::Mysql2Adapter + include ActiveRecord::ConnectionAdapters::MysqlBase +end \ No newline at end of file diff --git a/lib/activerecord-import/active_record/adapters/mysql_adapter.rb b/lib/activerecord-import/active_record/adapters/mysql_adapter.rb index 02327c1..20b345e 100644 --- a/lib/activerecord-import/active_record/adapters/mysql_adapter.rb +++ b/lib/activerecord-import/active_record/adapters/mysql_adapter.rb @@ -1,47 +1,6 @@ require "active_record/connection_adapters/mysql_adapter" +require "activerecord-import/active_record/adapters/mysql_base" class ActiveRecord::ConnectionAdapters::MysqlAdapter - include ActiveRecord::Extensions::Import::ImportSupport - include ActiveRecord::Extensions::Import::OnDuplicateKeyUpdateSupport - - # Returns a generated ON DUPLICATE KEY UPDATE statement given the passed - # in +args+. - def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc: - sql = ' ON DUPLICATE KEY UPDATE ' - arg = args.first - if arg.is_a?( Array ) - sql << sql_for_on_duplicate_key_update_as_array( table_name, arg ) - elsif arg.is_a?( Hash ) - sql << sql_for_on_duplicate_key_update_as_hash( table_name, arg ) - elsif arg.is_a?( String ) - sql << arg - else - raise ArgumentError.new( "Expected Array or Hash" ) - end - sql - end - - def sql_for_on_duplicate_key_update_as_array( table_name, arr ) # :nodoc: - results = arr.map do |column| - qc = quote_column_name( column ) - "#{table_name}.#{qc}=VALUES(#{qc})" - end - results.join( ',' ) - end - - def sql_for_on_duplicate_key_update_as_hash( table_name, hsh ) # :nodoc: - sql = ' ON DUPLICATE KEY UPDATE ' - results = hsh.map do |column1, column2| - qc1 = quote_column_name( column1 ) - qc2 = quote_column_name( column2 ) - "#{table_name}.#{qc1}=VALUES( #{qc2} )" - end - results.join( ',') - end - - #return true if the statement is a duplicate key record error - def duplicate_key_update_error?(exception)# :nodoc: - exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry') - end - + include ActiveRecord::ConnectionAdapters::MysqlBase end diff --git a/lib/activerecord-import/active_record/adapters/mysql_base.rb b/lib/activerecord-import/active_record/adapters/mysql_base.rb new file mode 100644 index 0000000..cbac429 --- /dev/null +++ b/lib/activerecord-import/active_record/adapters/mysql_base.rb @@ -0,0 +1,48 @@ +module ActiveRecord::ConnectionAdapters::MysqlBase + def self.included(klass) + klass.instance_eval do + include ActiveRecord::Extensions::Import::ImportSupport + include ActiveRecord::Extensions::Import::OnDuplicateKeyUpdateSupport + end + end + + # Returns a generated ON DUPLICATE KEY UPDATE statement given the passed + # in +args+. + def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc: + sql = ' ON DUPLICATE KEY UPDATE ' + arg = args.first + if arg.is_a?( Array ) + sql << sql_for_on_duplicate_key_update_as_array( table_name, arg ) + elsif arg.is_a?( Hash ) + sql << sql_for_on_duplicate_key_update_as_hash( table_name, arg ) + elsif arg.is_a?( String ) + sql << arg + else + raise ArgumentError.new( "Expected Array or Hash" ) + end + sql + end + + def sql_for_on_duplicate_key_update_as_array( table_name, arr ) # :nodoc: + results = arr.map do |column| + qc = quote_column_name( column ) + "#{table_name}.#{qc}=VALUES(#{qc})" + end + results.join( ',' ) + end + + def sql_for_on_duplicate_key_update_as_hash( table_name, hsh ) # :nodoc: + sql = ' ON DUPLICATE KEY UPDATE ' + results = hsh.map do |column1, column2| + qc1 = quote_column_name( column1 ) + qc2 = quote_column_name( column2 ) + "#{table_name}.#{qc1}=VALUES( #{qc2} )" + end + results.join( ',') + end + + #return true if the statement is a duplicate key record error + def duplicate_key_update_error?(exception)# :nodoc: + exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry') + end +end diff --git a/lib/activerecord-import/mysql2.rb b/lib/activerecord-import/mysql2.rb new file mode 100644 index 0000000..bf42665 --- /dev/null +++ b/lib/activerecord-import/mysql2.rb @@ -0,0 +1,2 @@ +require File.join File.dirname(__FILE__), "base" +ActiveRecord::Extensions.require_adapter "mysql2" diff --git a/test/adapters/mysql2.rb b/test/adapters/mysql2.rb new file mode 100644 index 0000000..0ddab79 --- /dev/null +++ b/test/adapters/mysql2.rb @@ -0,0 +1 @@ +ENV["ARE_DB"] = "mysql2" \ No newline at end of file diff --git a/test/database.yml b/test/database.yml index 73d5df9..8be291b 100644 --- a/test/database.yml +++ b/test/database.yml @@ -9,6 +9,10 @@ mysql: <<: *common adapter: mysql +mysql2: + <<: *common + adapter: mysql2 + postgresql: <<: *common username: zdennis diff --git a/test/mysql/import_test.rb b/test/mysql/import_test.rb index 692218c..37201d1 100644 --- a/test/mysql/import_test.rb +++ b/test/mysql/import_test.rb @@ -1,118 +1,4 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper') require "activerecord-import/mysql" -describe "#import with :on_duplicate_key_update option (mysql specific functionality)" do - extend ActiveSupport::TestCase::MySQLAssertions - - asssertion_group(:should_support_on_duplicate_key_update) do - should_not_update_fields_not_mentioned - should_update_foreign_keys - should_not_update_created_at_on_timestamp_columns - should_update_updated_at_on_timestamp_columns - end - - macro(:perform_import){ raise "supply your own #perform_import in a context below" } - macro(:updated_topic){ Topic.find(@topic) } - - context "given columns and values with :validation checks turned off" do - let(:columns){ %w( id title author_name author_email_address parent_id ) } - let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] } - let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] } - - macro(:perform_import) do |*opts| - Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false) - end - - setup do - Topic.import columns, values, :validate => false - @topic = Topic.find 99 - end - - context "using string column names" do - let(:update_columns){ [ "title", "author_email_address", "parent_id" ] } - should_support_on_duplicate_key_update - should_update_fields_mentioned - end - - context "using symbol column names" do - let(:update_columns){ [ :title, :author_email_address, :parent_id ] } - should_support_on_duplicate_key_update - should_update_fields_mentioned - end - - context "using string hash map" do - let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } } - should_support_on_duplicate_key_update - should_update_fields_mentioned - end - - context "using string hash map, but specifying column mismatches" do - let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } } - should_support_on_duplicate_key_update - should_update_fields_mentioned_with_hash_mappings - end - - context "using symbol hash map" do - let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } } - should_support_on_duplicate_key_update - should_update_fields_mentioned - end - - context "using symbol hash map, but specifying column mismatches" do - let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } } - should_support_on_duplicate_key_update - should_update_fields_mentioned_with_hash_mappings - end - end - - context "given array of model instances with :validation checks turned off" do - macro(:perform_import) do |*opts| - @topic.title = "Book - 2nd Edition" - @topic.author_name = "Author Should Not Change" - @topic.author_email_address = "johndoe@example.com" - @topic.parent_id = 57 - Topic.import [@topic], opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false) - end - - setup do - @topic = Generate(:topic, :id => 99, :author_name => "John Doe", :parent_id => 17) - end - - context "using string column names" do - let(:update_columns){ [ "title", "author_email_address", "parent_id" ] } - should_support_on_duplicate_key_update - should_update_fields_mentioned - end - - context "using symbol column names" do - let(:update_columns){ [ :title, :author_email_address, :parent_id ] } - should_support_on_duplicate_key_update - should_update_fields_mentioned - end - - context "using string hash map" do - let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } } - should_support_on_duplicate_key_update - should_update_fields_mentioned - end - - context "using string hash map, but specifying column mismatches" do - let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } } - should_support_on_duplicate_key_update - should_update_fields_mentioned_with_hash_mappings - end - - context "using symbol hash map" do - let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } } - should_support_on_duplicate_key_update - should_update_fields_mentioned - end - - context "using symbol hash map, but specifying column mismatches" do - let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } } - should_support_on_duplicate_key_update - should_update_fields_mentioned_with_hash_mappings - end - end - -end \ No newline at end of file +should_support_mysql_import_functionality \ No newline at end of file diff --git a/test/mysql2/import_test.rb b/test/mysql2/import_test.rb new file mode 100644 index 0000000..b3587b4 --- /dev/null +++ b/test/mysql2/import_test.rb @@ -0,0 +1,7 @@ +require File.expand_path(File.dirname(__FILE__) + '/../test_helper') +require "activerecord-import/mysql2" + +require "test/support/mysql/assertions" +require "test/support/mysql/import_examples" + +should_support_mysql_import_functionality \ No newline at end of file diff --git a/test/support/mysql/import_examples.rb b/test/support/mysql/import_examples.rb new file mode 100644 index 0000000..e8bb1ef --- /dev/null +++ b/test/support/mysql/import_examples.rb @@ -0,0 +1,117 @@ +def should_support_mysql_import_functionality + describe "#import with :on_duplicate_key_update option (mysql specific functionality)" do + extend ActiveSupport::TestCase::MySQLAssertions + + asssertion_group(:should_support_on_duplicate_key_update) do + should_not_update_fields_not_mentioned + should_update_foreign_keys + should_not_update_created_at_on_timestamp_columns + should_update_updated_at_on_timestamp_columns + end + + macro(:perform_import){ raise "supply your own #perform_import in a context below" } + macro(:updated_topic){ Topic.find(@topic) } + + context "given columns and values with :validation checks turned off" do + let(:columns){ %w( id title author_name author_email_address parent_id ) } + let(:values){ [ [ 99, "Book", "John Doe", "john@doe.com", 17 ] ] } + let(:updated_values){ [ [ 99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57 ] ] } + + macro(:perform_import) do |*opts| + Topic.import columns, updated_values, opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false) + end + + setup do + Topic.import columns, values, :validate => false + @topic = Topic.find 99 + end + + context "using string column names" do + let(:update_columns){ [ "title", "author_email_address", "parent_id" ] } + should_support_on_duplicate_key_update + should_update_fields_mentioned + end + + context "using symbol column names" do + let(:update_columns){ [ :title, :author_email_address, :parent_id ] } + should_support_on_duplicate_key_update + should_update_fields_mentioned + end + + context "using string hash map" do + let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } } + should_support_on_duplicate_key_update + should_update_fields_mentioned + end + + context "using string hash map, but specifying column mismatches" do + let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } } + should_support_on_duplicate_key_update + should_update_fields_mentioned_with_hash_mappings + end + + context "using symbol hash map" do + let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } } + should_support_on_duplicate_key_update + should_update_fields_mentioned + end + + context "using symbol hash map, but specifying column mismatches" do + let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } } + should_support_on_duplicate_key_update + should_update_fields_mentioned_with_hash_mappings + end + end + + context "given array of model instances with :validation checks turned off" do + macro(:perform_import) do |*opts| + @topic.title = "Book - 2nd Edition" + @topic.author_name = "Author Should Not Change" + @topic.author_email_address = "johndoe@example.com" + @topic.parent_id = 57 + Topic.import [@topic], opts.extract_options!.merge(:on_duplicate_key_update => update_columns, :validate => false) + end + + setup do + @topic = Generate(:topic, :id => 99, :author_name => "John Doe", :parent_id => 17) + end + + context "using string column names" do + let(:update_columns){ [ "title", "author_email_address", "parent_id" ] } + should_support_on_duplicate_key_update + should_update_fields_mentioned + end + + context "using symbol column names" do + let(:update_columns){ [ :title, :author_email_address, :parent_id ] } + should_support_on_duplicate_key_update + should_update_fields_mentioned + end + + context "using string hash map" do + let(:update_columns){ { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } } + should_support_on_duplicate_key_update + should_update_fields_mentioned + end + + context "using string hash map, but specifying column mismatches" do + let(:update_columns){ { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } } + should_support_on_duplicate_key_update + should_update_fields_mentioned_with_hash_mappings + end + + context "using symbol hash map" do + let(:update_columns){ { :title => :title, :author_email_address => :author_email_address, :parent_id => :parent_id } } + should_support_on_duplicate_key_update + should_update_fields_mentioned + end + + context "using symbol hash map, but specifying column mismatches" do + let(:update_columns){ { :title => :author_email_address, :author_email_address => :title, :parent_id => :parent_id } } + should_support_on_duplicate_key_update + should_update_fields_mentioned_with_hash_mappings + end + end + + end +end \ No newline at end of file