activerecord-import 0.14.1 → 1.4.1
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 +107 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +74 -8
- data/Brewfile +3 -1
- data/CHANGELOG.md +448 -2
- data/Gemfile +26 -19
- data/LICENSE +21 -56
- data/README.markdown +568 -32
- data/Rakefile +5 -1
- data/activerecord-import.gemspec +8 -7
- data/benchmarks/README +2 -2
- data/benchmarks/benchmark.rb +8 -1
- data/benchmarks/lib/base.rb +2 -0
- data/benchmarks/lib/cli_parser.rb +5 -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/gemfiles/4.2.gemfile +4 -3
- data/gemfiles/5.0.gemfile +4 -3
- data/gemfiles/5.1.gemfile +4 -0
- data/gemfiles/5.2.gemfile +4 -0
- data/gemfiles/6.0.gemfile +4 -0
- data/gemfiles/6.1.gemfile +4 -0
- data/gemfiles/7.0.gemfile +4 -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 +8 -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/adapters/abstract_adapter.rb +12 -16
- 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 +35 -18
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +107 -28
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +141 -18
- data/lib/activerecord-import/base.rb +15 -8
- data/lib/activerecord-import/import.rb +642 -178
- 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 +5 -3
- data/lib/activerecord-import/value_sets_parser.rb +29 -3
- data/lib/activerecord-import/version.rb +3 -1
- data/lib/activerecord-import.rb +5 -16
- data/test/adapters/jdbcmysql.rb +2 -0
- data/test/adapters/jdbcpostgresql.rb +2 -0
- data/test/adapters/jdbcsqlite3.rb +3 -0
- data/test/adapters/makara_postgis.rb +3 -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/{travis → github}/database.yml +7 -1
- data/test/import_test.rb +498 -32
- data/test/jdbcmysql/import_test.rb +2 -1
- data/test/jdbcpostgresql/import_test.rb +2 -1
- data/test/jdbcsqlite3/import_test.rb +6 -0
- data/test/makara_postgis/import_test.rb +10 -0
- data/test/models/account.rb +5 -0
- data/test/models/alarm.rb +4 -0
- data/test/models/animal.rb +8 -0
- data/test/models/bike_maker.rb +9 -0
- data/test/models/book.rb +4 -0
- data/test/models/car.rb +5 -0
- data/test/models/card.rb +5 -0
- data/test/models/chapter.rb +2 -0
- data/test/models/customer.rb +8 -0
- data/test/models/deck.rb +8 -0
- data/test/models/dictionary.rb +6 -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 +8 -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 +7 -0
- data/test/models/tag_alias.rb +5 -0
- data/test/models/topic.rb +16 -0
- data/test/models/user.rb +5 -0
- data/test/models/user_token.rb +6 -0
- data/test/models/vendor.rb +9 -0
- data/test/models/widget.rb +18 -0
- data/test/mysql2/import_test.rb +2 -0
- data/test/mysql2_makara/import_test.rb +2 -0
- data/test/mysqlspatial2/import_test.rb +2 -0
- data/test/postgis/import_test.rb +6 -0
- data/test/postgresql/import_test.rb +2 -4
- data/test/schema/generic_schema.rb +88 -3
- data/test/schema/jdbcpostgresql_schema.rb +3 -0
- data/test/schema/mysql2_schema.rb +21 -0
- data/test/schema/postgis_schema.rb +3 -0
- data/test/schema/postgresql_schema.rb +63 -0
- data/test/schema/sqlite3_schema.rb +15 -0
- data/test/schema/version.rb +2 -0
- data/test/sqlite3/import_test.rb +4 -50
- data/test/support/active_support/test_case_extensions.rb +8 -1
- data/test/support/assertions.rb +2 -0
- data/test/support/factories.rb +17 -8
- data/test/support/generate.rb +10 -8
- data/test/support/mysql/import_examples.rb +17 -3
- data/test/support/postgresql/import_examples.rb +442 -9
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +45 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +278 -1
- data/test/support/shared_examples/recursive_import.rb +137 -12
- data/test/support/sqlite3/import_examples.rb +232 -0
- data/test/synchronize_test.rb +10 -0
- data/test/test_helper.rb +44 -3
- data/test/value_sets_bytes_parser_test.rb +15 -2
- data/test/value_sets_records_parser_test.rb +2 -0
- metadata +74 -22
- data/.travis.yml +0 -52
- data/gemfiles/3.2.gemfile +0 -3
- data/gemfiles/4.0.gemfile +0 -3
- data/gemfiles/4.1.gemfile +0 -3
- data/test/schema/mysql_schema.rb +0 -16
data/activerecord-import.gemspec
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
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|
|
|
5
6
|
gem.authors = ["Zach Dennis"]
|
|
6
7
|
gem.email = ["zach.dennis@gmail.com"]
|
|
7
|
-
gem.summary = "Bulk
|
|
8
|
-
gem.description = "
|
|
9
|
-
gem.homepage = "
|
|
10
|
-
gem.license = "
|
|
8
|
+
gem.summary = "Bulk insert extension for ActiveRecord"
|
|
9
|
+
gem.description = "A library for bulk inserting data using ActiveRecord."
|
|
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/README
CHANGED
|
@@ -15,10 +15,10 @@ See "ruby benchmark.rb -h" for the complete listing of options.
|
|
|
15
15
|
EXAMPLES
|
|
16
16
|
--------
|
|
17
17
|
To output to html format:
|
|
18
|
-
ruby benchmark.rb --adapter=
|
|
18
|
+
ruby benchmark.rb --adapter=mysql2 --to-html=results.html
|
|
19
19
|
|
|
20
20
|
To output to csv format:
|
|
21
|
-
ruby benchmark.rb --adapter=
|
|
21
|
+
ruby benchmark.rb --adapter=mysql2 --to-csv=results.csv
|
|
22
22
|
|
|
23
23
|
LIMITATIONS
|
|
24
24
|
-----------
|
data/benchmarks/benchmark.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'pathname'
|
|
2
4
|
require "fileutils"
|
|
3
5
|
require "active_record"
|
|
6
|
+
require "active_record/base"
|
|
4
7
|
|
|
5
8
|
benchmark_dir = File.dirname(__FILE__)
|
|
6
9
|
|
|
@@ -19,7 +22,11 @@ FileUtils.mkdir_p 'log'
|
|
|
19
22
|
ActiveRecord::Base.configurations["test"] = YAML.load_file(File.join(benchmark_dir, "../test/database.yml"))[options.adapter]
|
|
20
23
|
ActiveRecord::Base.logger = Logger.new("log/test.log")
|
|
21
24
|
ActiveRecord::Base.logger.level = Logger::DEBUG
|
|
22
|
-
ActiveRecord
|
|
25
|
+
if ActiveRecord.respond_to?(:default_timezone)
|
|
26
|
+
ActiveRecord.default_timezone = :utc
|
|
27
|
+
else
|
|
28
|
+
ActiveRecord::Base.default_timezone = :utc
|
|
29
|
+
end
|
|
23
30
|
|
|
24
31
|
require "activerecord-import"
|
|
25
32
|
ActiveRecord::Base.establish_connection(:test)
|
data/benchmarks/lib/base.rb
CHANGED
|
@@ -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
|
|
@@ -42,7 +44,8 @@ module BenchmarkOptionParser
|
|
|
42
44
|
table_types: {},
|
|
43
45
|
delete_on_finish: true,
|
|
44
46
|
number_of_objects: [],
|
|
45
|
-
outputs: []
|
|
47
|
+
outputs: []
|
|
48
|
+
)
|
|
46
49
|
|
|
47
50
|
opt_parser = OptionParser.new do |opts|
|
|
48
51
|
opts.banner = BANNER
|
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/gemfiles/4.2.gemfile
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
gem 'activerecord', '~> 4.2.0'
|
|
4
|
+
gem 'composite_primary_keys', '~> 8.0'
|
data/gemfiles/5.0.gemfile
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
gem 'activerecord', '~> 5.0.0'
|
|
4
|
+
gem 'composite_primary_keys', '~> 9.0'
|
|
@@ -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,10 +1,12 @@
|
|
|
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)
|
|
4
6
|
%(#{sequence_name}.nextval)
|
|
5
7
|
end
|
|
6
8
|
|
|
7
|
-
def insert_many( sql, values, *args ) # :nodoc:
|
|
9
|
+
def insert_many( sql, values, _options = {}, *args ) # :nodoc:
|
|
8
10
|
number_of_inserts = 1
|
|
9
11
|
|
|
10
12
|
base_sql, post_sql = if sql.is_a?( String )
|
|
@@ -16,14 +18,13 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
16
18
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
17
19
|
insert( sql2insert, *args )
|
|
18
20
|
|
|
19
|
-
[number_of_inserts, []]
|
|
21
|
+
ActiveRecord::Import::Result.new([], number_of_inserts, [], [])
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
def pre_sql_statements(options)
|
|
23
25
|
sql = []
|
|
24
26
|
sql << options[:pre_sql] if options[:pre_sql]
|
|
25
27
|
sql << options[:command] if options[:command]
|
|
26
|
-
sql << "IGNORE" if options[:ignore]
|
|
27
28
|
|
|
28
29
|
# add keywords like IGNORE or DELAYED
|
|
29
30
|
if options[:keywords].is_a?(Array)
|
|
@@ -45,15 +46,10 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
45
46
|
def post_sql_statements( table_name, options ) # :nodoc:
|
|
46
47
|
post_sql_statements = []
|
|
47
48
|
|
|
48
|
-
if supports_on_duplicate_key_update?
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
post_sql_statements << sql_for_on_duplicate_key_ignore( table_name, options[:on_duplicate_key_ignore] )
|
|
53
|
-
end
|
|
54
|
-
elsif options[:on_duplicate_key_update]
|
|
55
|
-
post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update] )
|
|
56
|
-
end
|
|
49
|
+
if supports_on_duplicate_key_update? && options[:on_duplicate_key_update]
|
|
50
|
+
post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update], options[:primary_key], options[:locking_column] )
|
|
51
|
+
elsif logger && options[:on_duplicate_key_update]
|
|
52
|
+
logger.warn "Ignoring on_duplicate_key_update because it is not supported by the database."
|
|
57
53
|
end
|
|
58
54
|
|
|
59
55
|
# custom user post_sql
|
|
@@ -65,10 +61,10 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
65
61
|
post_sql_statements
|
|
66
62
|
end
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
def increment_locking_column!(table_name, results, locking_column)
|
|
65
|
+
if locking_column.present?
|
|
66
|
+
results << "\"#{locking_column}\"=#{table_name}.\"#{locking_column}\"+1"
|
|
67
|
+
end
|
|
72
68
|
end
|
|
73
69
|
|
|
74
70
|
def supports_on_duplicate_key_update?
|
|
@@ -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
|
|
@@ -7,7 +9,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
7
9
|
|
|
8
10
|
# +sql+ can be a single string or an array. If it is an array all
|
|
9
11
|
# elements that are in position >= 1 will be appended to the final SQL.
|
|
10
|
-
def insert_many( sql, values, *args ) # :nodoc:
|
|
12
|
+
def insert_many( sql, values, options = {}, *args ) # :nodoc:
|
|
11
13
|
# the number of inserts default
|
|
12
14
|
number_of_inserts = 0
|
|
13
15
|
|
|
@@ -31,7 +33,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
31
33
|
max = max_allowed_packet
|
|
32
34
|
|
|
33
35
|
# if we can insert it all as one statement
|
|
34
|
-
if NO_MAX_PACKET == max || total_bytes
|
|
36
|
+
if NO_MAX_PACKET == max || total_bytes <= max || options[:force_single_insert]
|
|
35
37
|
number_of_inserts += 1
|
|
36
38
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
37
39
|
insert( sql2insert, *args )
|
|
@@ -39,49 +41,56 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
39
41
|
value_sets = ::ActiveRecord::Import::ValueSetsBytesParser.parse(values,
|
|
40
42
|
reserved_bytes: sql_size,
|
|
41
43
|
max_bytes: max)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
|
|
45
|
+
transaction(requires_new: true) do
|
|
46
|
+
value_sets.each do |value_set|
|
|
47
|
+
number_of_inserts += 1
|
|
48
|
+
sql2insert = base_sql + value_set.join( ',' ) + post_sql
|
|
49
|
+
insert( sql2insert, *args )
|
|
50
|
+
end
|
|
46
51
|
end
|
|
47
52
|
end
|
|
48
53
|
|
|
49
|
-
[number_of_inserts, []]
|
|
54
|
+
ActiveRecord::Import::Result.new([], number_of_inserts, [], [])
|
|
50
55
|
end
|
|
51
56
|
|
|
52
57
|
# Returns the maximum number of bytes that the server will allow
|
|
53
58
|
# in a single packet
|
|
54
59
|
def max_allowed_packet # :nodoc:
|
|
55
60
|
@max_allowed_packet ||= begin
|
|
56
|
-
result = execute( "
|
|
61
|
+
result = execute( "SELECT @@max_allowed_packet" )
|
|
57
62
|
# original Mysql gem responds to #fetch_row while Mysql2 responds to #first
|
|
58
|
-
val = result.respond_to?(:fetch_row) ? result.fetch_row[
|
|
63
|
+
val = result.respond_to?(:fetch_row) ? result.fetch_row[0] : result.first[0]
|
|
59
64
|
val.to_i
|
|
60
65
|
end
|
|
61
66
|
end
|
|
62
67
|
|
|
68
|
+
def pre_sql_statements( options)
|
|
69
|
+
sql = []
|
|
70
|
+
sql << "IGNORE" if options[:ignore] || options[:on_duplicate_key_ignore]
|
|
71
|
+
sql + super
|
|
72
|
+
end
|
|
73
|
+
|
|
63
74
|
# Add a column to be updated on duplicate key update
|
|
64
75
|
def add_column_for_on_duplicate_key_update( column, options = {} ) # :nodoc:
|
|
65
|
-
if options
|
|
66
|
-
columns = options[:on_duplicate_key_update]
|
|
76
|
+
if (columns = options[:on_duplicate_key_update])
|
|
67
77
|
case columns
|
|
68
78
|
when Array then columns << column.to_sym unless columns.include?(column.to_sym)
|
|
69
79
|
when Hash then columns[column.to_sym] = column.to_sym
|
|
70
80
|
end
|
|
71
|
-
else
|
|
72
|
-
options[:on_duplicate_key_update] = [column.to_sym]
|
|
73
81
|
end
|
|
74
82
|
end
|
|
75
83
|
|
|
76
84
|
# Returns a generated ON DUPLICATE KEY UPDATE statement given the passed
|
|
77
85
|
# in +args+.
|
|
78
86
|
def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
|
|
79
|
-
sql = ' ON DUPLICATE KEY UPDATE '
|
|
87
|
+
sql = ' ON DUPLICATE KEY UPDATE '.dup
|
|
80
88
|
arg = args.first
|
|
89
|
+
locking_column = args.last
|
|
81
90
|
if arg.is_a?( Array )
|
|
82
|
-
sql << sql_for_on_duplicate_key_update_as_array( table_name, arg )
|
|
91
|
+
sql << sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arg )
|
|
83
92
|
elsif arg.is_a?( Hash )
|
|
84
|
-
sql << sql_for_on_duplicate_key_update_as_hash( table_name, arg )
|
|
93
|
+
sql << sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, arg )
|
|
85
94
|
elsif arg.is_a?( String )
|
|
86
95
|
sql << arg
|
|
87
96
|
else
|
|
@@ -90,20 +99,22 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
90
99
|
sql
|
|
91
100
|
end
|
|
92
101
|
|
|
93
|
-
def sql_for_on_duplicate_key_update_as_array( table_name, arr ) # :nodoc:
|
|
102
|
+
def sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arr ) # :nodoc:
|
|
94
103
|
results = arr.map do |column|
|
|
95
104
|
qc = quote_column_name( column )
|
|
96
105
|
"#{table_name}.#{qc}=VALUES(#{qc})"
|
|
97
106
|
end
|
|
107
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
98
108
|
results.join( ',' )
|
|
99
109
|
end
|
|
100
110
|
|
|
101
|
-
def sql_for_on_duplicate_key_update_as_hash( table_name, hsh ) # :nodoc:
|
|
111
|
+
def sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, hsh ) # :nodoc:
|
|
102
112
|
results = hsh.map do |column1, column2|
|
|
103
113
|
qc1 = quote_column_name( column1 )
|
|
104
114
|
qc2 = quote_column_name( column2 )
|
|
105
115
|
"#{table_name}.#{qc1}=VALUES( #{qc2} )"
|
|
106
116
|
end
|
|
117
|
+
increment_locking_column!(table_name, results, locking_column)
|
|
107
118
|
results.join( ',')
|
|
108
119
|
end
|
|
109
120
|
|
|
@@ -111,4 +122,10 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
111
122
|
def duplicate_key_update_error?(exception) # :nodoc:
|
|
112
123
|
exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry')
|
|
113
124
|
end
|
|
125
|
+
|
|
126
|
+
def increment_locking_column!(table_name, results, locking_column)
|
|
127
|
+
if locking_column.present?
|
|
128
|
+
results << "`#{locking_column}`=#{table_name}.`#{locking_column}`+1"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
114
131
|
end
|