activerecord-import 0.25.0 → 1.7.0
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.
- checksums.yaml +5 -5
- data/.github/workflows/test.yaml +151 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +74 -8
- data/.rubocop_todo.yml +10 -16
- data/Brewfile +3 -1
- data/CHANGELOG.md +232 -2
- data/Dockerfile +23 -0
- data/Gemfile +26 -14
- data/LICENSE +21 -56
- data/README.markdown +612 -21
- data/Rakefile +4 -1
- data/activerecord-import.gemspec +6 -5
- data/benchmarks/benchmark.rb +10 -4
- data/benchmarks/lib/base.rb +4 -2
- data/benchmarks/lib/cli_parser.rb +4 -2
- data/benchmarks/lib/float.rb +2 -0
- data/benchmarks/lib/mysql2_benchmark.rb +2 -0
- data/benchmarks/lib/output_to_csv.rb +2 -0
- data/benchmarks/lib/output_to_html.rb +4 -2
- data/benchmarks/models/test_innodb.rb +2 -0
- data/benchmarks/models/test_memory.rb +2 -0
- data/benchmarks/models/test_myisam.rb +2 -0
- data/benchmarks/schema/{mysql_schema.rb → mysql2_schema.rb} +2 -0
- data/docker-compose.yml +34 -0
- data/gemfiles/4.2.gemfile +2 -0
- data/gemfiles/5.0.gemfile +2 -0
- data/gemfiles/5.1.gemfile +2 -0
- data/gemfiles/5.2.gemfile +2 -0
- data/gemfiles/6.0.gemfile +4 -0
- data/gemfiles/6.1.gemfile +4 -0
- data/gemfiles/7.0.gemfile +4 -0
- data/gemfiles/7.1.gemfile +3 -0
- data/lib/activerecord-import/active_record/adapters/abstract_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb +6 -4
- data/lib/activerecord-import/active_record/adapters/jdbcpostgresql_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/jdbcsqlite3_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/postgresql_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/seamless_database_pool_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/trilogy_adapter.rb +8 -0
- data/lib/activerecord-import/adapters/abstract_adapter.rb +15 -6
- data/lib/activerecord-import/adapters/em_mysql2_adapter.rb +2 -0
- data/lib/activerecord-import/adapters/mysql2_adapter.rb +2 -0
- data/lib/activerecord-import/adapters/mysql_adapter.rb +34 -29
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +74 -55
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +138 -13
- data/lib/activerecord-import/adapters/trilogy_adapter.rb +7 -0
- data/lib/activerecord-import/base.rb +11 -2
- data/lib/activerecord-import/import.rb +290 -114
- data/lib/activerecord-import/mysql2.rb +2 -0
- data/lib/activerecord-import/postgresql.rb +2 -0
- data/lib/activerecord-import/sqlite3.rb +2 -0
- data/lib/activerecord-import/synchronize.rb +4 -2
- data/lib/activerecord-import/value_sets_parser.rb +5 -0
- data/lib/activerecord-import/version.rb +3 -1
- data/lib/activerecord-import.rb +2 -1
- data/test/adapters/jdbcmysql.rb +2 -0
- data/test/adapters/jdbcpostgresql.rb +2 -0
- data/test/adapters/jdbcsqlite3.rb +2 -0
- data/test/adapters/makara_postgis.rb +2 -0
- data/test/adapters/mysql2.rb +2 -0
- data/test/adapters/mysql2_makara.rb +2 -0
- data/test/adapters/mysql2spatial.rb +2 -0
- data/test/adapters/postgis.rb +2 -0
- data/test/adapters/postgresql.rb +2 -0
- data/test/adapters/postgresql_makara.rb +2 -0
- data/test/adapters/seamless_database_pool.rb +2 -0
- data/test/adapters/spatialite.rb +2 -0
- data/test/adapters/sqlite3.rb +2 -0
- data/test/adapters/trilogy.rb +9 -0
- data/test/database.yml.sample +7 -0
- data/test/{travis → github}/database.yml +7 -1
- data/test/import_test.rb +151 -8
- data/test/jdbcmysql/import_test.rb +5 -3
- data/test/jdbcpostgresql/import_test.rb +4 -2
- data/test/jdbcsqlite3/import_test.rb +4 -2
- data/test/makara_postgis/import_test.rb +4 -2
- data/test/models/account.rb +2 -0
- data/test/models/alarm.rb +2 -0
- data/test/models/animal.rb +8 -0
- data/test/models/author.rb +7 -0
- data/test/models/bike_maker.rb +3 -0
- data/test/models/book.rb +7 -2
- data/test/models/car.rb +2 -0
- data/test/models/card.rb +5 -0
- data/test/models/chapter.rb +2 -0
- data/test/models/composite_book.rb +19 -0
- data/test/models/composite_chapter.rb +9 -0
- data/test/models/customer.rb +18 -0
- data/test/models/deck.rb +8 -0
- data/test/models/dictionary.rb +2 -0
- data/test/models/discount.rb +2 -0
- data/test/models/end_note.rb +2 -0
- data/test/models/group.rb +2 -0
- data/test/models/order.rb +17 -0
- data/test/models/playing_card.rb +4 -0
- data/test/models/promotion.rb +2 -0
- data/test/models/question.rb +2 -0
- data/test/models/rule.rb +2 -0
- data/test/models/tag.rb +9 -1
- data/test/models/tag_alias.rb +11 -0
- data/test/models/topic.rb +7 -0
- data/test/models/user.rb +2 -0
- data/test/models/user_token.rb +3 -0
- data/test/models/vendor.rb +2 -0
- data/test/models/widget.rb +2 -0
- data/test/mysql2/import_test.rb +5 -3
- data/test/mysql2_makara/import_test.rb +5 -3
- data/test/mysqlspatial2/import_test.rb +5 -3
- data/test/postgis/import_test.rb +4 -2
- data/test/postgresql/import_test.rb +4 -2
- data/test/schema/generic_schema.rb +37 -1
- data/test/schema/jdbcpostgresql_schema.rb +3 -1
- data/test/schema/mysql2_schema.rb +2 -0
- data/test/schema/postgis_schema.rb +3 -1
- data/test/schema/postgresql_schema.rb +49 -0
- data/test/schema/sqlite3_schema.rb +15 -0
- data/test/schema/version.rb +2 -0
- data/test/sqlite3/import_test.rb +4 -2
- data/test/support/active_support/test_case_extensions.rb +2 -0
- data/test/support/assertions.rb +2 -0
- data/test/support/factories.rb +10 -8
- data/test/support/generate.rb +10 -8
- data/test/support/mysql/import_examples.rb +2 -1
- data/test/support/postgresql/import_examples.rb +152 -3
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +2 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +122 -9
- data/test/support/shared_examples/recursive_import.rb +128 -2
- data/test/support/sqlite3/import_examples.rb +191 -26
- data/test/synchronize_test.rb +2 -0
- data/test/test_helper.rb +34 -7
- data/test/trilogy/import_test.rb +7 -0
- data/test/value_sets_bytes_parser_test.rb +3 -1
- data/test/value_sets_records_parser_test.rb +3 -1
- metadata +46 -16
- data/.travis.yml +0 -71
- data/gemfiles/3.2.gemfile +0 -2
- data/gemfiles/4.0.gemfile +0 -2
- data/gemfiles/4.1.gemfile +0 -2
data/activerecord-import.gemspec
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require File.expand_path('../lib/activerecord-import/version', __FILE__)
|
|
3
4
|
|
|
4
5
|
Gem::Specification.new do |gem|
|
|
@@ -6,8 +7,8 @@ Gem::Specification.new do |gem|
|
|
|
6
7
|
gem.email = ["zach.dennis@gmail.com"]
|
|
7
8
|
gem.summary = "Bulk insert extension for ActiveRecord"
|
|
8
9
|
gem.description = "A library for bulk inserting data using ActiveRecord."
|
|
9
|
-
gem.homepage = "
|
|
10
|
-
gem.license = "
|
|
10
|
+
gem.homepage = "https://github.com/zdennis/activerecord-import"
|
|
11
|
+
gem.license = "MIT"
|
|
11
12
|
|
|
12
13
|
gem.files = `git ls-files`.split($\)
|
|
13
14
|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
|
@@ -16,8 +17,8 @@ Gem::Specification.new do |gem|
|
|
|
16
17
|
gem.require_paths = ["lib"]
|
|
17
18
|
gem.version = ActiveRecord::Import::VERSION
|
|
18
19
|
|
|
19
|
-
gem.required_ruby_version = ">=
|
|
20
|
+
gem.required_ruby_version = ">= 2.4.0"
|
|
20
21
|
|
|
21
|
-
gem.add_runtime_dependency "activerecord", ">=
|
|
22
|
+
gem.add_runtime_dependency "activerecord", ">= 4.2"
|
|
22
23
|
gem.add_development_dependency "rake"
|
|
23
24
|
end
|
data/benchmarks/benchmark.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'pathname'
|
|
2
4
|
require "fileutils"
|
|
3
5
|
require "active_record"
|
|
@@ -20,7 +22,11 @@ FileUtils.mkdir_p 'log'
|
|
|
20
22
|
ActiveRecord::Base.configurations["test"] = YAML.load_file(File.join(benchmark_dir, "../test/database.yml"))[options.adapter]
|
|
21
23
|
ActiveRecord::Base.logger = Logger.new("log/test.log")
|
|
22
24
|
ActiveRecord::Base.logger.level = Logger::DEBUG
|
|
23
|
-
ActiveRecord
|
|
25
|
+
if ActiveRecord.respond_to?(:default_timezone)
|
|
26
|
+
ActiveRecord.default_timezone = :utc
|
|
27
|
+
else
|
|
28
|
+
ActiveRecord::Base.default_timezone = :utc
|
|
29
|
+
end
|
|
24
30
|
|
|
25
31
|
require "activerecord-import"
|
|
26
32
|
ActiveRecord::Base.establish_connection(:test)
|
|
@@ -35,7 +41,7 @@ require File.join(benchmark_dir, "../test/schema/generic_schema")
|
|
|
35
41
|
adapter_schema = File.join(benchmark_dir, "schema/#{options.adapter}_schema.rb")
|
|
36
42
|
require adapter_schema if File.exist?(adapter_schema)
|
|
37
43
|
|
|
38
|
-
Dir[File.dirname(__FILE__)
|
|
44
|
+
Dir["#{File.dirname(__FILE__)}/models/*.rb"].sort.each { |file| require file }
|
|
39
45
|
|
|
40
46
|
require File.join( benchmark_dir, 'lib', "#{options.adapter}_benchmark" )
|
|
41
47
|
|
|
@@ -47,8 +53,8 @@ else
|
|
|
47
53
|
end
|
|
48
54
|
|
|
49
55
|
letter = options.adapter[0].chr
|
|
50
|
-
clazz_str = letter.upcase + options.adapter[1
|
|
51
|
-
clazz = Object.const_get( clazz_str
|
|
56
|
+
clazz_str = letter.upcase + options.adapter[1..].downcase
|
|
57
|
+
clazz = Object.const_get( "#{clazz_str}Benchmark" )
|
|
52
58
|
|
|
53
59
|
benchmarks = []
|
|
54
60
|
options.number_of_objects.each do |num|
|
data/benchmarks/lib/base.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class BenchmarkBase
|
|
2
4
|
attr_reader :results
|
|
3
5
|
|
|
@@ -25,11 +27,11 @@ class BenchmarkBase
|
|
|
25
27
|
# An OpenStruct object with the following attributes:
|
|
26
28
|
# * description - the description of the benchmark ran
|
|
27
29
|
# * tms - a Benchmark::Tms containing the results of the benchmark
|
|
28
|
-
def bm( description )
|
|
30
|
+
def bm( description, &block )
|
|
29
31
|
tms = nil
|
|
30
32
|
puts "Benchmarking #{description}"
|
|
31
33
|
|
|
32
|
-
Benchmark.bm { |x| tms = x.report
|
|
34
|
+
Benchmark.bm { |x| tms = x.report(&block) }
|
|
33
35
|
delete_all
|
|
34
36
|
failed = false
|
|
35
37
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'optparse'
|
|
2
4
|
require 'ostruct'
|
|
3
5
|
|
|
@@ -8,7 +10,7 @@ require 'ostruct'
|
|
|
8
10
|
# * t - the table types to test. ie: myisam, innodb, memory, temporary, etc.
|
|
9
11
|
#
|
|
10
12
|
module BenchmarkOptionParser
|
|
11
|
-
BANNER = "Usage: ruby #{$0} [options]\nSee ruby #{$0} -h for more options."
|
|
13
|
+
BANNER = "Usage: ruby #{$0} [options]\nSee ruby #{$0} -h for more options."
|
|
12
14
|
|
|
13
15
|
def self.print_banner
|
|
14
16
|
puts BANNER
|
|
@@ -27,7 +29,7 @@ module BenchmarkOptionParser
|
|
|
27
29
|
print_valid_table_types( options, prefix: " " )
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
# TODO IMPLEMENT THIS
|
|
32
|
+
# TODO: IMPLEMENT THIS
|
|
31
33
|
def self.print_valid_table_types( options, hsh = { prefix: '' } )
|
|
32
34
|
if !options.table_types.keys.empty?
|
|
33
35
|
options.table_types.keys.sort.each { |type| puts hsh[:prefix].to_s + type.to_s }
|
data/benchmarks/lib/float.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'erb'
|
|
2
4
|
|
|
3
5
|
module OutputToHTML
|
|
4
|
-
TEMPLATE_HEADER = <<"EOT"
|
|
6
|
+
TEMPLATE_HEADER = <<"EOT"
|
|
5
7
|
<div>
|
|
6
8
|
All times are rounded to the nearest thousandth for display purposes. Speedups next to each time are computed
|
|
7
9
|
before any rounding occurs. Also, all speedup calculations are computed by comparing a given time against
|
|
@@ -9,7 +11,7 @@ module OutputToHTML
|
|
|
9
11
|
</div>
|
|
10
12
|
EOT
|
|
11
13
|
|
|
12
|
-
TEMPLATE = <<"EOT"
|
|
14
|
+
TEMPLATE = <<"EOT"
|
|
13
15
|
<style>
|
|
14
16
|
td#benchmarkTitle {
|
|
15
17
|
border: 1px solid black;
|
data/docker-compose.yml
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
version: "3.5"
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
mysql:
|
|
5
|
+
platform: linux/x86_64
|
|
6
|
+
image: mysql:5.7
|
|
7
|
+
volumes:
|
|
8
|
+
- mysql-data:/var/lib/mysql
|
|
9
|
+
ports:
|
|
10
|
+
- "3306:3306"
|
|
11
|
+
|
|
12
|
+
postgresql:
|
|
13
|
+
image: postgres:latest
|
|
14
|
+
volumes:
|
|
15
|
+
- postgresql-data:/var/lib/postgresql/data
|
|
16
|
+
ports:
|
|
17
|
+
- "5432:5432"
|
|
18
|
+
|
|
19
|
+
app:
|
|
20
|
+
build:
|
|
21
|
+
context: .
|
|
22
|
+
environment:
|
|
23
|
+
DB_HOST: mysql
|
|
24
|
+
AR_VERSION: 7.0
|
|
25
|
+
volumes:
|
|
26
|
+
- .:/usr/src/app
|
|
27
|
+
depends_on:
|
|
28
|
+
- mysql
|
|
29
|
+
- postgresql
|
|
30
|
+
command: tail -f /dev/null
|
|
31
|
+
|
|
32
|
+
volumes:
|
|
33
|
+
mysql-data:
|
|
34
|
+
postgresql-data:
|
data/gemfiles/4.2.gemfile
CHANGED
data/gemfiles/5.0.gemfile
CHANGED
data/gemfiles/5.1.gemfile
CHANGED
data/gemfiles/5.2.gemfile
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
require "activerecord-import/adapters/mysql_adapter"
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
require "active_record/connection_adapters/mysql2_adapter"
|
|
4
|
+
require "activerecord-import/adapters/mysql2_adapter"
|
|
5
|
+
|
|
6
|
+
class ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
|
7
|
+
include ActiveRecord::Import::Mysql2Adapter
|
|
6
8
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord::Import::AbstractAdapter
|
|
2
4
|
module InstanceMethods
|
|
3
5
|
def next_value_for_sequence(sequence_name)
|
|
@@ -7,10 +9,11 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
7
9
|
def insert_many( sql, values, _options = {}, *args ) # :nodoc:
|
|
8
10
|
number_of_inserts = 1
|
|
9
11
|
|
|
10
|
-
base_sql, post_sql =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
base_sql, post_sql = case sql
|
|
13
|
+
when String
|
|
14
|
+
[sql, '']
|
|
15
|
+
when Array
|
|
16
|
+
[sql.shift, sql.join( ' ' )]
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
@@ -45,8 +48,8 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
45
48
|
post_sql_statements = []
|
|
46
49
|
|
|
47
50
|
if supports_on_duplicate_key_update? && options[:on_duplicate_key_update]
|
|
48
|
-
post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update], options[:primary_key], options[:locking_column] )
|
|
49
|
-
elsif options[:on_duplicate_key_update]
|
|
51
|
+
post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update], options[:model], options[:primary_key], options[:locking_column] )
|
|
52
|
+
elsif logger && options[:on_duplicate_key_update]
|
|
50
53
|
logger.warn "Ignoring on_duplicate_key_update because it is not supported by the database."
|
|
51
54
|
end
|
|
52
55
|
|
|
@@ -59,6 +62,12 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
59
62
|
post_sql_statements
|
|
60
63
|
end
|
|
61
64
|
|
|
65
|
+
def increment_locking_column!(table_name, results, locking_column)
|
|
66
|
+
if locking_column.present?
|
|
67
|
+
results << "\"#{locking_column}\"=#{table_name}.\"#{locking_column}\"+1"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
62
71
|
def supports_on_duplicate_key_update?
|
|
63
72
|
false
|
|
64
73
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord::Import::MysqlAdapter
|
|
2
4
|
include ActiveRecord::Import::ImportSupport
|
|
3
5
|
include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
|
|
@@ -11,13 +13,14 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
11
13
|
# the number of inserts default
|
|
12
14
|
number_of_inserts = 0
|
|
13
15
|
|
|
14
|
-
base_sql, post_sql =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
base_sql, post_sql = case sql
|
|
17
|
+
when String
|
|
18
|
+
[sql, '']
|
|
19
|
+
when Array
|
|
20
|
+
[sql.shift, sql.join( ' ' )]
|
|
18
21
|
end
|
|
19
22
|
|
|
20
|
-
sql_size = QUERY_OVERHEAD + base_sql.
|
|
23
|
+
sql_size = QUERY_OVERHEAD + base_sql.bytesize + post_sql.bytesize
|
|
21
24
|
|
|
22
25
|
# the number of bytes the requested insert statement values will take up
|
|
23
26
|
values_in_bytes = values.sum(&:bytesize)
|
|
@@ -31,7 +34,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
31
34
|
max = max_allowed_packet
|
|
32
35
|
|
|
33
36
|
# if we can insert it all as one statement
|
|
34
|
-
if
|
|
37
|
+
if max == NO_MAX_PACKET || total_bytes <= max || options[:force_single_insert]
|
|
35
38
|
number_of_inserts += 1
|
|
36
39
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
37
40
|
insert( sql2insert, *args )
|
|
@@ -56,9 +59,9 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
56
59
|
# in a single packet
|
|
57
60
|
def max_allowed_packet # :nodoc:
|
|
58
61
|
@max_allowed_packet ||= begin
|
|
59
|
-
result = execute( "
|
|
62
|
+
result = execute( "SELECT @@max_allowed_packet" )
|
|
60
63
|
# original Mysql gem responds to #fetch_row while Mysql2 responds to #first
|
|
61
|
-
val = result.respond_to?(:fetch_row) ? result.fetch_row[
|
|
64
|
+
val = result.respond_to?(:fetch_row) ? result.fetch_row[0] : result.first[0]
|
|
62
65
|
val.to_i
|
|
63
66
|
end
|
|
64
67
|
end
|
|
@@ -71,28 +74,25 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
71
74
|
|
|
72
75
|
# Add a column to be updated on duplicate key update
|
|
73
76
|
def add_column_for_on_duplicate_key_update( column, options = {} ) # :nodoc:
|
|
74
|
-
if options
|
|
75
|
-
columns = options[:on_duplicate_key_update]
|
|
77
|
+
if (columns = options[:on_duplicate_key_update])
|
|
76
78
|
case columns
|
|
77
79
|
when Array then columns << column.to_sym unless columns.include?(column.to_sym)
|
|
78
80
|
when Hash then columns[column.to_sym] = column.to_sym
|
|
79
81
|
end
|
|
80
|
-
elsif !options[:ignore] && !options[:on_duplicate_key_ignore]
|
|
81
|
-
options[:on_duplicate_key_update] = [column.to_sym]
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
# Returns a generated ON DUPLICATE KEY UPDATE statement given the passed
|
|
86
86
|
# in +args+.
|
|
87
87
|
def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
|
|
88
|
-
sql = ' ON DUPLICATE KEY UPDATE '
|
|
89
|
-
arg = args
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
sql << sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arg )
|
|
93
|
-
|
|
94
|
-
sql << sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, arg )
|
|
95
|
-
|
|
88
|
+
sql = ' ON DUPLICATE KEY UPDATE '.dup
|
|
89
|
+
arg, model, _primary_key, locking_column = args
|
|
90
|
+
case arg
|
|
91
|
+
when Array
|
|
92
|
+
sql << sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, arg )
|
|
93
|
+
when Hash
|
|
94
|
+
sql << sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, arg )
|
|
95
|
+
when String
|
|
96
96
|
sql << arg
|
|
97
97
|
else
|
|
98
98
|
raise ArgumentError, "Expected Array or Hash"
|
|
@@ -100,22 +100,27 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
100
100
|
sql
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
def sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arr ) # :nodoc:
|
|
103
|
+
def sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, arr ) # :nodoc:
|
|
104
104
|
results = arr.map do |column|
|
|
105
|
-
|
|
105
|
+
original_column_name = model.attribute_alias?( column ) ? model.attribute_alias( column ) : column
|
|
106
|
+
qc = quote_column_name( original_column_name )
|
|
106
107
|
"#{table_name}.#{qc}=VALUES(#{qc})"
|
|
107
108
|
end
|
|
108
|
-
increment_locking_column!(
|
|
109
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
109
110
|
results.join( ',' )
|
|
110
111
|
end
|
|
111
112
|
|
|
112
|
-
def sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, hsh ) # :nodoc:
|
|
113
|
+
def sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, hsh ) # :nodoc:
|
|
113
114
|
results = hsh.map do |column1, column2|
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
original_column1_name = model.attribute_alias?( column1 ) ? model.attribute_alias( column1 ) : column1
|
|
116
|
+
qc1 = quote_column_name( original_column1_name )
|
|
117
|
+
|
|
118
|
+
original_column2_name = model.attribute_alias?( column2 ) ? model.attribute_alias( column2 ) : column2
|
|
119
|
+
qc2 = quote_column_name( original_column2_name )
|
|
120
|
+
|
|
116
121
|
"#{table_name}.#{qc1}=VALUES( #{qc2} )"
|
|
117
122
|
end
|
|
118
|
-
increment_locking_column!(
|
|
123
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
119
124
|
results.join( ',')
|
|
120
125
|
end
|
|
121
126
|
|
|
@@ -124,9 +129,9 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
124
129
|
exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry')
|
|
125
130
|
end
|
|
126
131
|
|
|
127
|
-
def increment_locking_column!(
|
|
132
|
+
def increment_locking_column!(table_name, results, locking_column)
|
|
128
133
|
if locking_column.present?
|
|
129
|
-
results << "
|
|
134
|
+
results << "`#{locking_column}`=#{table_name}.`#{locking_column}`+1"
|
|
130
135
|
end
|
|
131
136
|
end
|
|
132
137
|
end
|