activerecord-import 1.4.1 → 2.2.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 +4 -4
- data/.github/workflows/test.yaml +73 -15
- data/.gitignore +4 -0
- data/.rubocop.yml +10 -7
- data/.rubocop_todo.yml +10 -16
- data/CHANGELOG.md +77 -1
- data/Dockerfile +23 -0
- data/Gemfile +20 -18
- data/README.markdown +46 -7
- data/Rakefile +5 -0
- data/activerecord-import.gemspec +4 -0
- data/benchmarks/benchmark.rb +3 -5
- data/benchmarks/lib/base.rb +8 -5
- data/benchmarks/lib/cli_parser.rb +9 -7
- data/docker-compose.yml +34 -0
- data/gemfiles/7.1.gemfile +3 -0
- data/gemfiles/7.2.gemfile +3 -0
- data/gemfiles/8.0.gemfile +3 -0
- data/lib/activerecord-import/active_record/adapters/trilogy_adapter.rb +8 -0
- data/lib/activerecord-import/adapters/abstract_adapter.rb +6 -5
- data/lib/activerecord-import/adapters/mysql_adapter.rb +24 -18
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +26 -18
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +29 -23
- data/lib/activerecord-import/adapters/trilogy_adapter.rb +7 -0
- data/lib/activerecord-import/base.rb +4 -0
- data/lib/activerecord-import/import.rb +64 -36
- data/lib/activerecord-import/value_sets_parser.rb +1 -0
- data/lib/activerecord-import/version.rb +1 -1
- data/lib/activerecord-import.rb +0 -1
- data/test/adapters/janus_mysql2.rb +3 -0
- data/test/adapters/janus_trilogy.rb +11 -0
- data/test/adapters/mysql2_proxy.rb +3 -0
- data/test/adapters/postgresql_proxy.rb +3 -0
- data/test/adapters/trilogy.rb +11 -0
- data/test/database.yml.sample +21 -0
- data/test/github/database.yml +19 -2
- data/test/import_test.rb +15 -39
- data/test/janus_mysql2/import_test.rb +8 -0
- data/test/janus_trilogy/import_test.rb +7 -0
- data/test/jdbcmysql/import_test.rb +3 -3
- data/test/jdbcpostgresql/import_test.rb +2 -2
- data/test/jdbcsqlite3/import_test.rb +2 -2
- data/test/makara_postgis/import_test.rb +2 -2
- data/test/models/author.rb +9 -0
- data/test/models/bike_maker.rb +1 -0
- data/test/models/book.rb +10 -3
- data/test/models/composite_book.rb +19 -0
- data/test/models/composite_chapter.rb +12 -0
- data/test/models/customer.rb +14 -4
- data/test/models/order.rb +13 -4
- data/test/models/tag.rb +6 -1
- data/test/models/tag_alias.rb +7 -1
- data/test/models/topic.rb +6 -0
- data/test/models/widget.rb +10 -3
- data/test/mysql2/import_test.rb +3 -3
- data/test/mysql2_makara/import_test.rb +3 -3
- data/test/mysql2_proxy/import_test.rb +6 -0
- data/test/mysqlspatial2/import_test.rb +3 -3
- data/test/postgis/import_test.rb +2 -2
- data/test/postgresql/import_test.rb +2 -2
- data/test/postgresql_proxy/import_test.rb +6 -0
- data/test/schema/generic_schema.rb +4 -1
- data/test/schema/jdbcpostgresql_schema.rb +1 -1
- data/test/schema/postgis_schema.rb +1 -1
- data/test/schema/postgresql_schema.rb +35 -4
- data/test/sqlite3/import_test.rb +2 -2
- data/test/support/active_support/test_case_extensions.rb +1 -5
- data/test/support/mysql/import_examples.rb +6 -8
- data/test/support/postgresql/import_examples.rb +49 -53
- data/test/support/shared_examples/on_duplicate_key_update.rb +67 -10
- data/test/support/shared_examples/recursive_import.rb +106 -1
- data/test/test_helper.rb +13 -22
- data/test/trilogy/import_test.rb +7 -0
- data/test/value_sets_bytes_parser_test.rb +1 -1
- data/test/value_sets_records_parser_test.rb +1 -1
- metadata +37 -9
- data/gemfiles/4.2.gemfile +0 -4
- data/gemfiles/5.0.gemfile +0 -4
- data/gemfiles/5.1.gemfile +0 -4
- data/lib/activerecord-import/mysql2.rb +0 -9
- data/lib/activerecord-import/postgresql.rb +0 -9
- data/lib/activerecord-import/sqlite3.rb +0 -9
data/benchmarks/lib/base.rb
CHANGED
|
@@ -16,7 +16,7 @@ class BenchmarkBase
|
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# Returns
|
|
19
|
+
# Returns a struct which contains two attritues, +description+ and +tms+ after performing an
|
|
20
20
|
# actual benchmark.
|
|
21
21
|
#
|
|
22
22
|
# == PARAMETERS
|
|
@@ -24,18 +24,21 @@ class BenchmarkBase
|
|
|
24
24
|
# * blk - the block of code to benchmark
|
|
25
25
|
#
|
|
26
26
|
# == RETURNS
|
|
27
|
-
#
|
|
27
|
+
# A struct object with the following attributes:
|
|
28
28
|
# * description - the description of the benchmark ran
|
|
29
29
|
# * tms - a Benchmark::Tms containing the results of the benchmark
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
BmStruct = Struct.new( :description, :tms, :failed, keyword_init: true )
|
|
32
|
+
|
|
33
|
+
def bm( description, &block )
|
|
31
34
|
tms = nil
|
|
32
35
|
puts "Benchmarking #{description}"
|
|
33
36
|
|
|
34
|
-
Benchmark.bm { |x| tms = x.report
|
|
37
|
+
Benchmark.bm { |x| tms = x.report(&block) }
|
|
35
38
|
delete_all
|
|
36
39
|
failed = false
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
BmStruct.new( description: description, tms: tms, failed: failed )
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
# Given a model class (ie: Topic), and an array of columns and value sets
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'optparse'
|
|
4
|
-
require 'ostruct'
|
|
5
4
|
|
|
6
5
|
#
|
|
7
6
|
# == PARAMETERS
|
|
@@ -10,7 +9,7 @@ require 'ostruct'
|
|
|
10
9
|
# * t - the table types to test. ie: myisam, innodb, memory, temporary, etc.
|
|
11
10
|
#
|
|
12
11
|
module BenchmarkOptionParser
|
|
13
|
-
BANNER = "Usage: ruby #{$0} [options]\nSee ruby #{$0} -h for more options."
|
|
12
|
+
BANNER = "Usage: ruby #{$0} [options]\nSee ruby #{$0} -h for more options.".freeze
|
|
14
13
|
|
|
15
14
|
def self.print_banner
|
|
16
15
|
puts BANNER
|
|
@@ -29,7 +28,7 @@ module BenchmarkOptionParser
|
|
|
29
28
|
print_valid_table_types( options, prefix: " " )
|
|
30
29
|
end
|
|
31
30
|
|
|
32
|
-
# TODO IMPLEMENT THIS
|
|
31
|
+
# TODO: IMPLEMENT THIS
|
|
33
32
|
def self.print_valid_table_types( options, hsh = { prefix: '' } )
|
|
34
33
|
if !options.table_types.keys.empty?
|
|
35
34
|
options.table_types.keys.sort.each { |type| puts hsh[:prefix].to_s + type.to_s }
|
|
@@ -38,8 +37,11 @@ module BenchmarkOptionParser
|
|
|
38
37
|
end
|
|
39
38
|
end
|
|
40
39
|
|
|
40
|
+
OptionsStruct = Struct.new( :adapter, :table_types, :delete_on_finish, :number_of_objects, :outputs,
|
|
41
|
+
:benchmark_all_types, keyword_init: true )
|
|
42
|
+
OutputStruct = Struct.new( :format, :filename, keyword_init: true )
|
|
41
43
|
def self.parse( args )
|
|
42
|
-
options =
|
|
44
|
+
options = OptionsStruct.new(
|
|
43
45
|
adapter: 'mysql2',
|
|
44
46
|
table_types: {},
|
|
45
47
|
delete_on_finish: true,
|
|
@@ -81,12 +83,12 @@ module BenchmarkOptionParser
|
|
|
81
83
|
|
|
82
84
|
# print results in CSV format
|
|
83
85
|
opts.on( "--to-csv [String]", "Print results in a CSV file format" ) do |filename|
|
|
84
|
-
options.outputs <<
|
|
86
|
+
options.outputs << OutputStruct.new( format: 'csv', filename: filename)
|
|
85
87
|
end
|
|
86
88
|
|
|
87
89
|
# print results in HTML format
|
|
88
90
|
opts.on( "--to-html [String]", "Print results in HTML format" ) do |filename|
|
|
89
|
-
options.outputs <<
|
|
91
|
+
options.outputs << OutputStruct.new( format: 'html', filename: filename )
|
|
90
92
|
end
|
|
91
93
|
end # end opt.parse!
|
|
92
94
|
|
|
@@ -100,7 +102,7 @@ module BenchmarkOptionParser
|
|
|
100
102
|
end
|
|
101
103
|
|
|
102
104
|
options.number_of_objects = [1000] if options.number_of_objects.empty?
|
|
103
|
-
options.outputs = [
|
|
105
|
+
options.outputs = [OutputStruct.new( format: 'html', filename: 'benchmark.html')] if options.outputs.empty?
|
|
104
106
|
|
|
105
107
|
print_options( options )
|
|
106
108
|
|
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:
|
|
@@ -9,10 +9,11 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
9
9
|
def insert_many( sql, values, _options = {}, *args ) # :nodoc:
|
|
10
10
|
number_of_inserts = 1
|
|
11
11
|
|
|
12
|
-
base_sql, post_sql =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
base_sql, post_sql = case sql
|
|
13
|
+
when String
|
|
14
|
+
[sql, '']
|
|
15
|
+
when Array
|
|
16
|
+
[sql.shift, sql.join( ' ' )]
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
@@ -47,7 +48,7 @@ module ActiveRecord::Import::AbstractAdapter
|
|
|
47
48
|
post_sql_statements = []
|
|
48
49
|
|
|
49
50
|
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
|
+
post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update], options[:model], options[:primary_key], options[:locking_column] )
|
|
51
52
|
elsif logger && options[:on_duplicate_key_update]
|
|
52
53
|
logger.warn "Ignoring on_duplicate_key_update because it is not supported by the database."
|
|
53
54
|
end
|
|
@@ -13,13 +13,14 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
13
13
|
# the number of inserts default
|
|
14
14
|
number_of_inserts = 0
|
|
15
15
|
|
|
16
|
-
base_sql, post_sql =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
base_sql, post_sql = case sql
|
|
17
|
+
when String
|
|
18
|
+
[sql, '']
|
|
19
|
+
when Array
|
|
20
|
+
[sql.shift, sql.join( ' ' )]
|
|
20
21
|
end
|
|
21
22
|
|
|
22
|
-
sql_size = QUERY_OVERHEAD + base_sql.
|
|
23
|
+
sql_size = QUERY_OVERHEAD + base_sql.bytesize + post_sql.bytesize
|
|
23
24
|
|
|
24
25
|
# the number of bytes the requested insert statement values will take up
|
|
25
26
|
values_in_bytes = values.sum(&:bytesize)
|
|
@@ -33,7 +34,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
33
34
|
max = max_allowed_packet
|
|
34
35
|
|
|
35
36
|
# if we can insert it all as one statement
|
|
36
|
-
if
|
|
37
|
+
if max == NO_MAX_PACKET || total_bytes <= max || options[:force_single_insert]
|
|
37
38
|
number_of_inserts += 1
|
|
38
39
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
39
40
|
insert( sql2insert, *args )
|
|
@@ -85,13 +86,13 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
85
86
|
# in +args+.
|
|
86
87
|
def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
|
|
87
88
|
sql = ' ON DUPLICATE KEY UPDATE '.dup
|
|
88
|
-
arg = args
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
sql << sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arg )
|
|
92
|
-
|
|
93
|
-
sql << sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, arg )
|
|
94
|
-
|
|
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
|
|
95
96
|
sql << arg
|
|
96
97
|
else
|
|
97
98
|
raise ArgumentError, "Expected Array or Hash"
|
|
@@ -99,19 +100,24 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
99
100
|
sql
|
|
100
101
|
end
|
|
101
102
|
|
|
102
|
-
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:
|
|
103
104
|
results = arr.map do |column|
|
|
104
|
-
|
|
105
|
+
original_column_name = model.attribute_alias?( column ) ? model.attribute_alias( column ) : column
|
|
106
|
+
qc = quote_column_name( original_column_name )
|
|
105
107
|
"#{table_name}.#{qc}=VALUES(#{qc})"
|
|
106
108
|
end
|
|
107
109
|
increment_locking_column!(table_name, results, locking_column)
|
|
108
110
|
results.join( ',' )
|
|
109
111
|
end
|
|
110
112
|
|
|
111
|
-
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:
|
|
112
114
|
results = hsh.map do |column1, column2|
|
|
113
|
-
|
|
114
|
-
|
|
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
|
+
|
|
115
121
|
"#{table_name}.#{qc1}=VALUES( #{qc2} )"
|
|
116
122
|
end
|
|
117
123
|
increment_locking_column!(table_name, results, locking_column)
|
|
@@ -12,10 +12,11 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
12
12
|
ids = []
|
|
13
13
|
results = []
|
|
14
14
|
|
|
15
|
-
base_sql, post_sql =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
base_sql, post_sql = case sql
|
|
16
|
+
when String
|
|
17
|
+
[sql, '']
|
|
18
|
+
when Array
|
|
19
|
+
[sql.shift, sql.join( ' ' )]
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
@@ -110,13 +111,14 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
110
111
|
# Add a column to be updated on duplicate key update
|
|
111
112
|
def add_column_for_on_duplicate_key_update( column, options = {} ) # :nodoc:
|
|
112
113
|
arg = options[:on_duplicate_key_update]
|
|
113
|
-
|
|
114
|
+
case arg
|
|
115
|
+
when Hash
|
|
114
116
|
columns = arg.fetch( :columns ) { arg[:columns] = [] }
|
|
115
117
|
case columns
|
|
116
118
|
when Array then columns << column.to_sym unless columns.include?( column.to_sym )
|
|
117
119
|
when Hash then columns[column.to_sym] = column.to_sym
|
|
118
120
|
end
|
|
119
|
-
|
|
121
|
+
when Array
|
|
120
122
|
arg << column.to_sym unless arg.include?( column.to_sym )
|
|
121
123
|
end
|
|
122
124
|
end
|
|
@@ -132,7 +134,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
132
134
|
# Returns a generated ON CONFLICT DO UPDATE statement given the passed
|
|
133
135
|
# in +args+.
|
|
134
136
|
def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
|
|
135
|
-
arg, primary_key, locking_column = args
|
|
137
|
+
arg, model, primary_key, locking_column = args
|
|
136
138
|
arg = { columns: arg } if arg.is_a?( Array ) || arg.is_a?( String )
|
|
137
139
|
return unless arg.is_a?( Hash )
|
|
138
140
|
|
|
@@ -151,11 +153,12 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
151
153
|
end
|
|
152
154
|
|
|
153
155
|
sql << "#{conflict_target}DO UPDATE SET "
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
case columns
|
|
157
|
+
when Array
|
|
158
|
+
sql << sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, columns )
|
|
159
|
+
when Hash
|
|
160
|
+
sql << sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, columns )
|
|
161
|
+
when String
|
|
159
162
|
sql << columns
|
|
160
163
|
else
|
|
161
164
|
raise ArgumentError, 'Expected :columns to be an Array or Hash'
|
|
@@ -166,19 +169,24 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
166
169
|
sql
|
|
167
170
|
end
|
|
168
171
|
|
|
169
|
-
def sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arr ) # :nodoc:
|
|
172
|
+
def sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, arr ) # :nodoc:
|
|
170
173
|
results = arr.map do |column|
|
|
171
|
-
|
|
174
|
+
original_column_name = model.attribute_alias?( column ) ? model.attribute_alias( column ) : column
|
|
175
|
+
qc = quote_column_name( original_column_name )
|
|
172
176
|
"#{qc}=EXCLUDED.#{qc}"
|
|
173
177
|
end
|
|
174
178
|
increment_locking_column!(table_name, results, locking_column)
|
|
175
179
|
results.join( ',' )
|
|
176
180
|
end
|
|
177
181
|
|
|
178
|
-
def sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, hsh ) # :nodoc:
|
|
182
|
+
def sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, hsh ) # :nodoc:
|
|
179
183
|
results = hsh.map do |column1, column2|
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
original_column1_name = model.attribute_alias?( column1 ) ? model.attribute_alias( column1 ) : column1
|
|
185
|
+
qc1 = quote_column_name( original_column1_name )
|
|
186
|
+
|
|
187
|
+
original_column2_name = model.attribute_alias?( column2 ) ? model.attribute_alias( column2 ) : column2
|
|
188
|
+
qc2 = quote_column_name( original_column2_name )
|
|
189
|
+
|
|
182
190
|
"#{qc1}=EXCLUDED.#{qc2}"
|
|
183
191
|
end
|
|
184
192
|
increment_locking_column!(table_name, results, locking_column)
|
|
@@ -192,7 +200,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
192
200
|
if constraint_name.present?
|
|
193
201
|
"ON CONSTRAINT #{constraint_name} "
|
|
194
202
|
elsif conflict_target.present?
|
|
195
|
-
sql =
|
|
203
|
+
sql = "(#{Array( conflict_target ).reject( &:blank? ).join( ', ' )}) "
|
|
196
204
|
sql += "WHERE #{index_predicate} " if index_predicate
|
|
197
205
|
sql
|
|
198
206
|
end
|
|
@@ -24,10 +24,11 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
24
24
|
def insert_many( sql, values, _options = {}, *args ) # :nodoc:
|
|
25
25
|
number_of_inserts = 0
|
|
26
26
|
|
|
27
|
-
base_sql, post_sql =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
base_sql, post_sql = case sql
|
|
28
|
+
when String
|
|
29
|
+
[sql, '']
|
|
30
|
+
when Array
|
|
31
|
+
[sql.shift, sql.join( ' ' )]
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
value_sets = ::ActiveRecord::Import::ValueSetsRecordsParser.parse(values,
|
|
@@ -56,11 +57,9 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
56
57
|
def post_sql_statements( table_name, options ) # :nodoc:
|
|
57
58
|
sql = []
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
sql << sql_for_on_duplicate_key_ignore( options[:on_duplicate_key_ignore] )
|
|
63
|
-
end
|
|
60
|
+
# Options :recursive and :on_duplicate_key_ignore are mutually exclusive
|
|
61
|
+
if supports_on_duplicate_key_update? && (options[:ignore] || options[:on_duplicate_key_ignore]) && !options[:on_duplicate_key_update]
|
|
62
|
+
sql << sql_for_on_duplicate_key_ignore( options[:on_duplicate_key_ignore] )
|
|
64
63
|
end
|
|
65
64
|
|
|
66
65
|
sql + super
|
|
@@ -73,13 +72,14 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
73
72
|
# Add a column to be updated on duplicate key update
|
|
74
73
|
def add_column_for_on_duplicate_key_update( column, options = {} ) # :nodoc:
|
|
75
74
|
arg = options[:on_duplicate_key_update]
|
|
76
|
-
|
|
75
|
+
case arg
|
|
76
|
+
when Hash
|
|
77
77
|
columns = arg.fetch( :columns ) { arg[:columns] = [] }
|
|
78
78
|
case columns
|
|
79
79
|
when Array then columns << column.to_sym unless columns.include?( column.to_sym )
|
|
80
80
|
when Hash then columns[column.to_sym] = column.to_sym
|
|
81
81
|
end
|
|
82
|
-
|
|
82
|
+
when Array
|
|
83
83
|
arg << column.to_sym unless arg.include?( column.to_sym )
|
|
84
84
|
end
|
|
85
85
|
end
|
|
@@ -95,7 +95,7 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
95
95
|
# Returns a generated ON CONFLICT DO UPDATE statement given the passed
|
|
96
96
|
# in +args+.
|
|
97
97
|
def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
|
|
98
|
-
arg, primary_key, locking_column = args
|
|
98
|
+
arg, model, primary_key, locking_column = args
|
|
99
99
|
arg = { columns: arg } if arg.is_a?( Array ) || arg.is_a?( String )
|
|
100
100
|
return unless arg.is_a?( Hash )
|
|
101
101
|
|
|
@@ -114,11 +114,12 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
sql << "#{conflict_target}DO UPDATE SET "
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
case columns
|
|
118
|
+
when Array
|
|
119
|
+
sql << sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, columns )
|
|
120
|
+
when Hash
|
|
121
|
+
sql << sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, columns )
|
|
122
|
+
when String
|
|
122
123
|
sql << columns
|
|
123
124
|
else
|
|
124
125
|
raise ArgumentError, 'Expected :columns to be an Array or Hash'
|
|
@@ -129,19 +130,24 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
129
130
|
sql
|
|
130
131
|
end
|
|
131
132
|
|
|
132
|
-
def sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arr ) # :nodoc:
|
|
133
|
+
def sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, arr ) # :nodoc:
|
|
133
134
|
results = arr.map do |column|
|
|
134
|
-
|
|
135
|
+
original_column_name = model.attribute_alias?( column ) ? model.attribute_alias( column ) : column
|
|
136
|
+
qc = quote_column_name( original_column_name )
|
|
135
137
|
"#{qc}=EXCLUDED.#{qc}"
|
|
136
138
|
end
|
|
137
139
|
increment_locking_column!(table_name, results, locking_column)
|
|
138
140
|
results.join( ',' )
|
|
139
141
|
end
|
|
140
142
|
|
|
141
|
-
def sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, hsh ) # :nodoc:
|
|
143
|
+
def sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, hsh ) # :nodoc:
|
|
142
144
|
results = hsh.map do |column1, column2|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
original_column1_name = model.attribute_alias?( column1 ) ? model.attribute_alias( column1 ) : column1
|
|
146
|
+
qc1 = quote_column_name( original_column1_name )
|
|
147
|
+
|
|
148
|
+
original_column2_name = model.attribute_alias?( column2 ) ? model.attribute_alias( column2 ) : column2
|
|
149
|
+
qc2 = quote_column_name( original_column2_name )
|
|
150
|
+
|
|
145
151
|
"#{qc1}=EXCLUDED.#{qc2}"
|
|
146
152
|
end
|
|
147
153
|
increment_locking_column!(table_name, results, locking_column)
|
|
@@ -152,7 +158,7 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
152
158
|
conflict_target = args[:conflict_target]
|
|
153
159
|
index_predicate = args[:index_predicate]
|
|
154
160
|
if conflict_target.present?
|
|
155
|
-
sql =
|
|
161
|
+
sql = "(#{Array( conflict_target ).reject( &:blank? ).join( ', ' )}) "
|
|
156
162
|
sql += "WHERE #{index_predicate} " if index_predicate
|
|
157
163
|
sql
|
|
158
164
|
end
|
|
@@ -11,11 +11,15 @@ module ActiveRecord::Import
|
|
|
11
11
|
case adapter
|
|
12
12
|
when 'mysql2_makara' then 'mysql2'
|
|
13
13
|
when 'mysql2spatial' then 'mysql2'
|
|
14
|
+
when 'mysql2_proxy' then 'mysql2'
|
|
15
|
+
when 'janus_mysql2' then 'mysql2'
|
|
14
16
|
when 'spatialite' then 'sqlite3'
|
|
15
17
|
when 'postgresql_makara' then 'postgresql'
|
|
18
|
+
when 'postgresql_proxy' then 'postgresql'
|
|
16
19
|
when 'makara_postgis' then 'postgresql'
|
|
17
20
|
when 'postgis' then 'postgresql'
|
|
18
21
|
when 'cockroachdb' then 'postgresql'
|
|
22
|
+
when 'janus_trilogy' then 'trilogy'
|
|
19
23
|
else adapter
|
|
20
24
|
end
|
|
21
25
|
end
|