activerecord-import 2.2.0 → 3.0.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/CHANGELOG.md +124 -0
- data/README.markdown +99 -32
- data/lib/activerecord-import/active_record/adapters/mysql2_proxy_adapter.rb +8 -0
- data/lib/activerecord-import/active_record/adapters/postgresql_proxy_adapter.rb +8 -0
- data/lib/activerecord-import/active_record/adapters/sqlite3_proxy_adapter.rb +8 -0
- data/lib/activerecord-import/active_record/adapters/trilogy_proxy_adapter.rb +8 -0
- data/lib/activerecord-import/adapters/abstract_adapter.rb +58 -0
- data/lib/activerecord-import/adapters/active_record_proxy_adapter.rb +19 -0
- data/lib/activerecord-import/adapters/mysql2_proxy_adapter.rb +9 -0
- data/lib/activerecord-import/adapters/mysql_adapter.rb +70 -13
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +10 -52
- data/lib/activerecord-import/adapters/postgresql_proxy_adapter.rb +9 -0
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +7 -3
- data/lib/activerecord-import/adapters/sqlite3_proxy_adapter.rb +9 -0
- data/lib/activerecord-import/adapters/trilogy_proxy_adapter.rb +9 -0
- data/lib/activerecord-import/aliases.rb +42 -0
- data/lib/activerecord-import/base.rb +2 -46
- data/lib/activerecord-import/core.rb +62 -0
- data/lib/activerecord-import/import.rb +612 -369
- data/lib/activerecord-import/synchronize.rb +28 -16
- data/lib/activerecord-import/version.rb +1 -1
- data/lib/activerecord-import/without_aliases.rb +17 -0
- metadata +19 -211
- data/.github/workflows/test.yaml +0 -165
- data/.gitignore +0 -37
- data/.rubocop.yml +0 -118
- data/.rubocop_todo.yml +0 -30
- data/Brewfile +0 -5
- data/Dockerfile +0 -23
- data/Gemfile +0 -63
- data/Rakefile +0 -75
- data/activerecord-import.gemspec +0 -28
- data/benchmarks/README +0 -32
- data/benchmarks/benchmark.rb +0 -72
- data/benchmarks/lib/base.rb +0 -143
- data/benchmarks/lib/cli_parser.rb +0 -111
- data/benchmarks/lib/float.rb +0 -17
- data/benchmarks/lib/mysql2_benchmark.rb +0 -21
- data/benchmarks/lib/output_to_csv.rb +0 -21
- data/benchmarks/lib/output_to_html.rb +0 -66
- data/benchmarks/models/test_innodb.rb +0 -5
- data/benchmarks/models/test_memory.rb +0 -5
- data/benchmarks/models/test_myisam.rb +0 -5
- data/benchmarks/schema/mysql2_schema.rb +0 -18
- data/docker-compose.yml +0 -34
- data/gemfiles/5.2.gemfile +0 -4
- data/gemfiles/6.0.gemfile +0 -4
- data/gemfiles/6.1.gemfile +0 -4
- data/gemfiles/7.0.gemfile +0 -4
- data/gemfiles/7.1.gemfile +0 -3
- data/gemfiles/7.2.gemfile +0 -3
- data/gemfiles/8.0.gemfile +0 -3
- data/test/adapters/janus_mysql2.rb +0 -3
- data/test/adapters/janus_trilogy.rb +0 -11
- data/test/adapters/jdbcmysql.rb +0 -3
- data/test/adapters/jdbcpostgresql.rb +0 -3
- data/test/adapters/jdbcsqlite3.rb +0 -3
- data/test/adapters/makara_postgis.rb +0 -3
- data/test/adapters/mysql2.rb +0 -3
- data/test/adapters/mysql2_makara.rb +0 -3
- data/test/adapters/mysql2_proxy.rb +0 -3
- data/test/adapters/mysql2spatial.rb +0 -3
- data/test/adapters/postgis.rb +0 -3
- data/test/adapters/postgresql.rb +0 -3
- data/test/adapters/postgresql_makara.rb +0 -3
- data/test/adapters/postgresql_proxy.rb +0 -3
- data/test/adapters/seamless_database_pool.rb +0 -3
- data/test/adapters/spatialite.rb +0 -3
- data/test/adapters/sqlite3.rb +0 -3
- data/test/adapters/trilogy.rb +0 -11
- data/test/database.yml.sample +0 -73
- data/test/github/database.yml +0 -85
- data/test/import_test.rb +0 -970
- data/test/janus_mysql2/import_test.rb +0 -8
- data/test/janus_trilogy/import_test.rb +0 -7
- data/test/jdbcmysql/import_test.rb +0 -7
- data/test/jdbcpostgresql/import_test.rb +0 -6
- data/test/jdbcsqlite3/import_test.rb +0 -6
- data/test/makara_postgis/import_test.rb +0 -10
- data/test/models/account.rb +0 -5
- data/test/models/alarm.rb +0 -4
- data/test/models/animal.rb +0 -8
- data/test/models/author.rb +0 -9
- data/test/models/bike_maker.rb +0 -10
- data/test/models/book.rb +0 -18
- data/test/models/car.rb +0 -5
- data/test/models/card.rb +0 -5
- data/test/models/chapter.rb +0 -6
- data/test/models/composite_book.rb +0 -19
- data/test/models/composite_chapter.rb +0 -12
- data/test/models/customer.rb +0 -18
- data/test/models/deck.rb +0 -8
- data/test/models/dictionary.rb +0 -6
- data/test/models/discount.rb +0 -5
- data/test/models/end_note.rb +0 -6
- data/test/models/group.rb +0 -5
- data/test/models/order.rb +0 -17
- data/test/models/playing_card.rb +0 -4
- data/test/models/promotion.rb +0 -5
- data/test/models/question.rb +0 -5
- data/test/models/rule.rb +0 -5
- data/test/models/tag.rb +0 -12
- data/test/models/tag_alias.rb +0 -11
- data/test/models/topic.rb +0 -31
- data/test/models/user.rb +0 -5
- data/test/models/user_token.rb +0 -6
- data/test/models/vendor.rb +0 -9
- data/test/models/widget.rb +0 -33
- data/test/mysql2/import_test.rb +0 -7
- data/test/mysql2_makara/import_test.rb +0 -8
- data/test/mysql2_proxy/import_test.rb +0 -6
- data/test/mysqlspatial2/import_test.rb +0 -8
- data/test/postgis/import_test.rb +0 -10
- data/test/postgresql/import_test.rb +0 -6
- data/test/postgresql_proxy/import_test.rb +0 -6
- data/test/schema/generic_schema.rb +0 -230
- data/test/schema/jdbcpostgresql_schema.rb +0 -3
- data/test/schema/mysql2_schema.rb +0 -21
- data/test/schema/postgis_schema.rb +0 -3
- data/test/schema/postgresql_schema.rb +0 -94
- data/test/schema/sqlite3_schema.rb +0 -15
- data/test/schema/version.rb +0 -12
- data/test/sqlite3/import_test.rb +0 -6
- data/test/support/active_support/test_case_extensions.rb +0 -73
- data/test/support/assertions.rb +0 -75
- data/test/support/factories.rb +0 -66
- data/test/support/generate.rb +0 -31
- data/test/support/mysql/import_examples.rb +0 -97
- data/test/support/postgresql/import_examples.rb +0 -642
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +0 -45
- data/test/support/shared_examples/on_duplicate_key_update.rb +0 -437
- data/test/support/shared_examples/recursive_import.rb +0 -352
- data/test/support/sqlite3/import_examples.rb +0 -232
- data/test/synchronize_test.rb +0 -43
- data/test/test_helper.rb +0 -91
- data/test/trilogy/import_test.rb +0 -7
- data/test/value_sets_bytes_parser_test.rb +0 -106
- data/test/value_sets_records_parser_test.rb +0 -34
|
@@ -6,12 +6,17 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
6
6
|
|
|
7
7
|
NO_MAX_PACKET = 0
|
|
8
8
|
QUERY_OVERHEAD = 8 # This was shown to be true for MySQL, but it's not clear where the overhead is from.
|
|
9
|
+
MIN_MARIADB_VERSION_FOR_RETURNING = "10.5.0"
|
|
9
10
|
|
|
10
11
|
# +sql+ can be a single string or an array. If it is an array all
|
|
11
12
|
# elements that are in position >= 1 will be appended to the final SQL.
|
|
12
13
|
def insert_many( sql, values, options = {}, *args ) # :nodoc:
|
|
13
14
|
# the number of inserts default
|
|
14
15
|
number_of_inserts = 0
|
|
16
|
+
affected_rows = nil
|
|
17
|
+
returning = import_returning?( options )
|
|
18
|
+
returned_columns = []
|
|
19
|
+
returned_rows = []
|
|
15
20
|
|
|
16
21
|
base_sql, post_sql = case sql
|
|
17
22
|
when String
|
|
@@ -33,26 +38,55 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
33
38
|
|
|
34
39
|
max = max_allowed_packet
|
|
35
40
|
|
|
41
|
+
insert_value_set = lambda do |value_set|
|
|
42
|
+
number_of_inserts += 1
|
|
43
|
+
sql2insert = base_sql + value_set.join( ',' ) + post_sql
|
|
44
|
+
|
|
45
|
+
if returning
|
|
46
|
+
result = exec_query( sql2insert, *args )
|
|
47
|
+
returned_columns = result.columns
|
|
48
|
+
returned_rows.concat(result.rows)
|
|
49
|
+
|
|
50
|
+
if result.respond_to?(:affected_rows) && result.affected_rows
|
|
51
|
+
affected_rows = (affected_rows || 0) + result.affected_rows
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
# Unlike #execute, whose result only has the affected rows with some drivers
|
|
55
|
+
# (e.g. trilogy, but not mysql2), #exec_update returns them with every driver
|
|
56
|
+
count = exec_update( sql2insert, *args )
|
|
57
|
+
affected_rows = (affected_rows || 0) + count if count.is_a?(Integer)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
36
61
|
# if we can insert it all as one statement
|
|
37
62
|
if max == NO_MAX_PACKET || total_bytes <= max || options[:force_single_insert]
|
|
38
|
-
|
|
39
|
-
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
40
|
-
insert( sql2insert, *args )
|
|
63
|
+
insert_value_set.call(values)
|
|
41
64
|
else
|
|
42
65
|
value_sets = ::ActiveRecord::Import::ValueSetsBytesParser.parse(values,
|
|
43
66
|
reserved_bytes: sql_size,
|
|
44
67
|
max_bytes: max)
|
|
45
68
|
|
|
46
69
|
transaction(requires_new: true) do
|
|
47
|
-
value_sets.each
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
70
|
+
value_sets.each { |value_set| insert_value_set.call(value_set) }
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# ActiveRecord < 7.1 doesn't clear the query cache for #execute or #exec_query
|
|
75
|
+
clear_query_cache if query_cache_enabled
|
|
76
|
+
|
|
77
|
+
ids = []
|
|
78
|
+
results = []
|
|
79
|
+
|
|
80
|
+
if returning
|
|
81
|
+
returned_values = if returned_columns.size > 1
|
|
82
|
+
{ values: returned_rows, columns: returned_columns }
|
|
83
|
+
else
|
|
84
|
+
{ values: returned_rows.map(&:first) }
|
|
52
85
|
end
|
|
86
|
+
ids, results = ids_and_results_from_returned_values(returned_values, options)
|
|
53
87
|
end
|
|
54
88
|
|
|
55
|
-
ActiveRecord::Import::Result.new([], number_of_inserts,
|
|
89
|
+
ActiveRecord::Import::Result.new([], number_of_inserts, ids, results, affected_rows)
|
|
56
90
|
end
|
|
57
91
|
|
|
58
92
|
# Returns the maximum number of bytes that the server will allow
|
|
@@ -68,10 +102,33 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
68
102
|
|
|
69
103
|
def pre_sql_statements( options)
|
|
70
104
|
sql = []
|
|
71
|
-
|
|
105
|
+
# Recursive imports need the id of every model, which INSERT IGNORE doesn't return
|
|
106
|
+
# for ignored rows, so :ignore and :on_duplicate_key_ignore don't apply to them.
|
|
107
|
+
if (options[:ignore] || options[:on_duplicate_key_ignore]) && !(options[:recursive] && supports_setting_primary_key_of_imported_objects?)
|
|
108
|
+
sql << "IGNORE"
|
|
109
|
+
end
|
|
72
110
|
sql + super
|
|
73
111
|
end
|
|
74
112
|
|
|
113
|
+
def post_sql_statements( table_name, options ) # :nodoc:
|
|
114
|
+
sql = super
|
|
115
|
+
sql << "RETURNING #{returning_selections(options).join(', ')}" if import_returning?( options )
|
|
116
|
+
sql
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# MariaDB 10.5+ supports INSERT ... RETURNING, so the ids of imported models
|
|
120
|
+
# can be set. MySQL doesn't support it.
|
|
121
|
+
def supports_setting_primary_key_of_imported_objects?
|
|
122
|
+
respond_to?(:mariadb?) && mariadb? && database_version >= MIN_MARIADB_VERSION_FOR_RETURNING
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Returns true if the import should use INSERT ... RETURNING
|
|
126
|
+
def import_returning?( options ) # :nodoc:
|
|
127
|
+
supports_setting_primary_key_of_imported_objects? &&
|
|
128
|
+
returning_selections(options).present? &&
|
|
129
|
+
!(options[:no_returning] && !options[:recursive])
|
|
130
|
+
end
|
|
131
|
+
|
|
75
132
|
# Add a column to be updated on duplicate key update
|
|
76
133
|
def add_column_for_on_duplicate_key_update( column, options = {} ) # :nodoc:
|
|
77
134
|
if (columns = options[:on_duplicate_key_update])
|
|
@@ -102,7 +159,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
102
159
|
|
|
103
160
|
def sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, arr ) # :nodoc:
|
|
104
161
|
results = arr.map do |column|
|
|
105
|
-
original_column_name = model.
|
|
162
|
+
original_column_name = model.column_name_for_import( column )
|
|
106
163
|
qc = quote_column_name( original_column_name )
|
|
107
164
|
"#{table_name}.#{qc}=VALUES(#{qc})"
|
|
108
165
|
end
|
|
@@ -112,10 +169,10 @@ module ActiveRecord::Import::MysqlAdapter
|
|
|
112
169
|
|
|
113
170
|
def sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, hsh ) # :nodoc:
|
|
114
171
|
results = hsh.map do |column1, column2|
|
|
115
|
-
original_column1_name = model.
|
|
172
|
+
original_column1_name = model.column_name_for_import( column1 )
|
|
116
173
|
qc1 = quote_column_name( original_column1_name )
|
|
117
174
|
|
|
118
|
-
original_column2_name = model.
|
|
175
|
+
original_column2_name = model.column_name_for_import( column2 )
|
|
119
176
|
qc2 = quote_column_name( original_column2_name )
|
|
120
177
|
|
|
121
178
|
"#{table_name}.#{qc1}=VALUES( #{qc2} )"
|
|
@@ -9,8 +9,6 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
9
9
|
def insert_many( sql, values, options = {}, *args ) # :nodoc:
|
|
10
10
|
number_of_inserts = 1
|
|
11
11
|
returned_values = {}
|
|
12
|
-
ids = []
|
|
13
|
-
results = []
|
|
14
12
|
|
|
15
13
|
base_sql, post_sql = case sql
|
|
16
14
|
when String
|
|
@@ -23,7 +21,9 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
23
21
|
|
|
24
22
|
selections = returning_selections(options)
|
|
25
23
|
if selections.blank? || (options[:no_returning] && !options[:recursive])
|
|
26
|
-
insert
|
|
24
|
+
# Don't use #insert here: ActiveRecord appends its own
|
|
25
|
+
# RETURNING <primary key> clause to statements run through it.
|
|
26
|
+
exec_update( sql2insert, *args )
|
|
27
27
|
else
|
|
28
28
|
returned_values = if selections.size > 1
|
|
29
29
|
# Select composite columns
|
|
@@ -32,44 +32,14 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
32
32
|
else
|
|
33
33
|
{ values: select_values( sql2insert, *args ) }
|
|
34
34
|
end
|
|
35
|
-
clear_query_cache if query_cache_enabled
|
|
36
35
|
end
|
|
36
|
+
clear_query_cache if query_cache_enabled
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
ids = Array(returned_values[:values])
|
|
40
|
-
elsif options[:primary_key].blank?
|
|
41
|
-
options[:returning_columns] ||= returned_values[:columns]
|
|
42
|
-
results = Array(returned_values[:values])
|
|
43
|
-
else
|
|
44
|
-
# split primary key and returning columns
|
|
45
|
-
ids, results, options[:returning_columns] = split_ids_and_results(returned_values, options)
|
|
46
|
-
end
|
|
38
|
+
ids, results = ids_and_results_from_returned_values(returned_values, options)
|
|
47
39
|
|
|
48
40
|
ActiveRecord::Import::Result.new([], number_of_inserts, ids, results)
|
|
49
41
|
end
|
|
50
42
|
|
|
51
|
-
def split_ids_and_results( selections, options )
|
|
52
|
-
ids = []
|
|
53
|
-
returning_values = []
|
|
54
|
-
|
|
55
|
-
columns = Array(selections[:columns])
|
|
56
|
-
values = Array(selections[:values])
|
|
57
|
-
id_indexes = Array(options[:primary_key]).map { |key| columns.index(key) }
|
|
58
|
-
returning_columns = columns.reject.with_index { |_, index| id_indexes.include?(index) }
|
|
59
|
-
returning_indexes = returning_columns.map { |column| columns.index(column) }
|
|
60
|
-
|
|
61
|
-
values.each do |value|
|
|
62
|
-
value_array = Array(value)
|
|
63
|
-
ids << id_indexes.map { |index| value_array[index] }
|
|
64
|
-
returning_values << returning_indexes.map { |index| value_array[index] }
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
ids.map!(&:first) if id_indexes.size == 1
|
|
68
|
-
returning_values.map!(&:first) if returning_columns.size == 1
|
|
69
|
-
|
|
70
|
-
[ids, returning_values, returning_columns]
|
|
71
|
-
end
|
|
72
|
-
|
|
73
43
|
def next_value_for_sequence(sequence_name)
|
|
74
44
|
%{nextval('#{sequence_name}')}
|
|
75
45
|
end
|
|
@@ -93,19 +63,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
93
63
|
sql << " RETURNING #{selections.join(', ')}"
|
|
94
64
|
end
|
|
95
65
|
|
|
96
|
-
sql
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def returning_selections(options)
|
|
100
|
-
selections = []
|
|
101
|
-
column_names = Array(options[:model].column_names)
|
|
102
|
-
|
|
103
|
-
selections += Array(options[:primary_key]) if options[:primary_key].present?
|
|
104
|
-
selections += Array(options[:returning]) if options[:returning].present?
|
|
105
|
-
|
|
106
|
-
selections.map do |selection|
|
|
107
|
-
column_names.include?(selection.to_s) ? "\"#{selection}\"" : selection
|
|
108
|
-
end
|
|
66
|
+
sql.uniq
|
|
109
67
|
end
|
|
110
68
|
|
|
111
69
|
# Add a column to be updated on duplicate key update
|
|
@@ -171,7 +129,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
171
129
|
|
|
172
130
|
def sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, arr ) # :nodoc:
|
|
173
131
|
results = arr.map do |column|
|
|
174
|
-
original_column_name = model.
|
|
132
|
+
original_column_name = model.column_name_for_import( column )
|
|
175
133
|
qc = quote_column_name( original_column_name )
|
|
176
134
|
"#{qc}=EXCLUDED.#{qc}"
|
|
177
135
|
end
|
|
@@ -181,10 +139,10 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
181
139
|
|
|
182
140
|
def sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, hsh ) # :nodoc:
|
|
183
141
|
results = hsh.map do |column1, column2|
|
|
184
|
-
original_column1_name = model.
|
|
142
|
+
original_column1_name = model.column_name_for_import( column1 )
|
|
185
143
|
qc1 = quote_column_name( original_column1_name )
|
|
186
144
|
|
|
187
|
-
original_column2_name = model.
|
|
145
|
+
original_column2_name = model.column_name_for_import( column2 )
|
|
188
146
|
qc2 = quote_column_name( original_column2_name )
|
|
189
147
|
|
|
190
148
|
"#{qc1}=EXCLUDED.#{qc2}"
|
|
@@ -207,7 +165,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
207
165
|
end
|
|
208
166
|
|
|
209
167
|
def sql_for_default_conflict_target( table_name, primary_key )
|
|
210
|
-
conflict_target = Array(primary_key).join(', ')
|
|
168
|
+
conflict_target = Array(primary_key).map { |key| quote_column_name(key) }.join(', ')
|
|
211
169
|
"(#{conflict_target}) " if conflict_target.present?
|
|
212
170
|
end
|
|
213
171
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "activerecord-import/adapters/postgresql_adapter"
|
|
4
|
+
require "activerecord-import/adapters/active_record_proxy_adapter"
|
|
5
|
+
|
|
6
|
+
module ActiveRecord::Import::PostgreSQLProxyAdapter
|
|
7
|
+
include ActiveRecord::Import::PostgreSQLAdapter
|
|
8
|
+
include ActiveRecord::Import::ActiveRecordProxyAdapter
|
|
9
|
+
end
|
|
@@ -19,6 +19,10 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
19
19
|
database_version >= MIN_VERSION_FOR_UPSERT
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def supports_default_keyword_in_values?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
22
26
|
# +sql+ can be a single string or an array. If it is an array all
|
|
23
27
|
# elements that are in position >= 1 will be appended to the final SQL.
|
|
24
28
|
def insert_many( sql, values, _options = {}, *args ) # :nodoc:
|
|
@@ -132,7 +136,7 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
132
136
|
|
|
133
137
|
def sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, arr ) # :nodoc:
|
|
134
138
|
results = arr.map do |column|
|
|
135
|
-
original_column_name = model.
|
|
139
|
+
original_column_name = model.column_name_for_import( column )
|
|
136
140
|
qc = quote_column_name( original_column_name )
|
|
137
141
|
"#{qc}=EXCLUDED.#{qc}"
|
|
138
142
|
end
|
|
@@ -142,10 +146,10 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
142
146
|
|
|
143
147
|
def sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, hsh ) # :nodoc:
|
|
144
148
|
results = hsh.map do |column1, column2|
|
|
145
|
-
original_column1_name = model.
|
|
149
|
+
original_column1_name = model.column_name_for_import( column1 )
|
|
146
150
|
qc1 = quote_column_name( original_column1_name )
|
|
147
151
|
|
|
148
|
-
original_column2_name = model.
|
|
152
|
+
original_column2_name = model.column_name_for_import( column2 )
|
|
149
153
|
qc2 = quote_column_name( original_column2_name )
|
|
150
154
|
|
|
151
155
|
"#{qc1}=EXCLUDED.#{qc2}"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "activerecord-import/adapters/sqlite3_adapter"
|
|
4
|
+
require "activerecord-import/adapters/active_record_proxy_adapter"
|
|
5
|
+
|
|
6
|
+
module ActiveRecord::Import::SQLite3ProxyAdapter
|
|
7
|
+
include ActiveRecord::Import::SQLite3Adapter
|
|
8
|
+
include ActiveRecord::Import::ActiveRecordProxyAdapter
|
|
9
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "activerecord-import/adapters/trilogy_adapter"
|
|
4
|
+
require "activerecord-import/adapters/active_record_proxy_adapter"
|
|
5
|
+
|
|
6
|
+
module ActiveRecord::Import::TrilogyProxyAdapter
|
|
7
|
+
include ActiveRecord::Import::TrilogyAdapter
|
|
8
|
+
include ActiveRecord::Import::ActiveRecordProxyAdapter
|
|
9
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord::Import
|
|
4
|
+
# Methods aliased to +bulk_import+ and +bulk_import!+, keyed by the class
|
|
5
|
+
# they're defined on.
|
|
6
|
+
ALIASES = {
|
|
7
|
+
ActiveRecord::Associations::CollectionProxy => { import: :bulk_import },
|
|
8
|
+
ActiveRecord::Associations::CollectionAssociation => { import: :bulk_import },
|
|
9
|
+
ActiveRecord::Base.singleton_class => { import: :bulk_import, import!: :bulk_import! }
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
# Aliases +import+ and +import!+ to +bulk_import+ and +bulk_import!+, unless
|
|
13
|
+
# they are already defined or aliases have been skipped.
|
|
14
|
+
def self.define_aliases
|
|
15
|
+
return if skip_aliases?
|
|
16
|
+
|
|
17
|
+
ALIASES.each do |klass, aliases|
|
|
18
|
+
aliases.each do |name, target|
|
|
19
|
+
klass.send(:alias_method, name, target) unless klass.method_defined?(name)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Prevents the +import+ and +import!+ aliases from being defined, and removes
|
|
25
|
+
# them if they already have been. Used by "activerecord-import/without_aliases".
|
|
26
|
+
def self.skip_aliases!
|
|
27
|
+
@skip_aliases = true
|
|
28
|
+
|
|
29
|
+
ALIASES.each do |klass, aliases|
|
|
30
|
+
aliases.each do |name, target|
|
|
31
|
+
next unless klass.method_defined?(name)
|
|
32
|
+
|
|
33
|
+
method = klass.instance_method(name)
|
|
34
|
+
klass.send(:remove_method, name) if method.owner == klass && method.original_name == target
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.skip_aliases?
|
|
40
|
+
@skip_aliases == true
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -1,49 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
4
|
-
require "active_record"
|
|
5
|
-
require "active_record/version"
|
|
3
|
+
require "activerecord-import/core"
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
ADAPTER_PATH = "activerecord-import/active_record/adapters"
|
|
9
|
-
|
|
10
|
-
def self.base_adapter(adapter)
|
|
11
|
-
case adapter
|
|
12
|
-
when 'mysql2_makara' then 'mysql2'
|
|
13
|
-
when 'mysql2spatial' then 'mysql2'
|
|
14
|
-
when 'mysql2_proxy' then 'mysql2'
|
|
15
|
-
when 'janus_mysql2' then 'mysql2'
|
|
16
|
-
when 'spatialite' then 'sqlite3'
|
|
17
|
-
when 'postgresql_makara' then 'postgresql'
|
|
18
|
-
when 'postgresql_proxy' then 'postgresql'
|
|
19
|
-
when 'makara_postgis' then 'postgresql'
|
|
20
|
-
when 'postgis' then 'postgresql'
|
|
21
|
-
when 'cockroachdb' then 'postgresql'
|
|
22
|
-
when 'janus_trilogy' then 'trilogy'
|
|
23
|
-
else adapter
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Loads the import functionality for a specific database adapter
|
|
28
|
-
def self.require_adapter(adapter)
|
|
29
|
-
require File.join(ADAPTER_PATH, "/#{base_adapter(adapter)}_adapter")
|
|
30
|
-
rescue LoadError
|
|
31
|
-
# fallback
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Loads the import functionality for the passed in ActiveRecord connection
|
|
35
|
-
def self.load_from_connection_pool(connection_pool)
|
|
36
|
-
adapter =
|
|
37
|
-
if connection_pool.respond_to?(:db_config) # ActiveRecord >= 6.1
|
|
38
|
-
connection_pool.db_config.adapter
|
|
39
|
-
else
|
|
40
|
-
connection_pool.spec.config[:adapter]
|
|
41
|
-
end
|
|
42
|
-
require_adapter adapter
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
require 'activerecord-import/import'
|
|
47
|
-
require 'activerecord-import/active_record/adapters/abstract_adapter'
|
|
48
|
-
require 'activerecord-import/synchronize'
|
|
49
|
-
require 'activerecord-import/value_sets_parser'
|
|
5
|
+
ActiveRecord::Import.define_aliases
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "concurrent/map"
|
|
5
|
+
require "active_record"
|
|
6
|
+
require "active_record/version"
|
|
7
|
+
|
|
8
|
+
module ActiveRecord::Import
|
|
9
|
+
ADAPTER_PATH = "activerecord-import/active_record/adapters"
|
|
10
|
+
ATTEMPTED_ADAPTERS = Concurrent::Map.new # :nodoc:
|
|
11
|
+
|
|
12
|
+
def self.base_adapter(adapter)
|
|
13
|
+
case adapter
|
|
14
|
+
when 'mysql2_makara' then 'mysql2'
|
|
15
|
+
when 'mysql2spatial' then 'mysql2'
|
|
16
|
+
when 'janus_mysql2' then 'mysql2'
|
|
17
|
+
when 'spatialite' then 'sqlite3'
|
|
18
|
+
when 'postgresql_makara' then 'postgresql'
|
|
19
|
+
when 'makara_postgis' then 'postgresql'
|
|
20
|
+
when 'postgis' then 'postgresql'
|
|
21
|
+
when 'cockroachdb' then 'postgresql'
|
|
22
|
+
when 'janus_trilogy' then 'trilogy'
|
|
23
|
+
else adapter
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Loads the import functionality for a specific database adapter
|
|
28
|
+
def self.require_adapter(adapter)
|
|
29
|
+
require File.join(ADAPTER_PATH, "/#{base_adapter(adapter)}_adapter")
|
|
30
|
+
rescue LoadError
|
|
31
|
+
# fallback
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Loads the import functionality for the passed in ActiveRecord connection
|
|
35
|
+
def self.load_from_connection_pool(connection_pool)
|
|
36
|
+
require_adapter adapter_for(connection_pool)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Loads the import functionality for the passed in ActiveRecord connection,
|
|
40
|
+
# unless it's been tried for the connection's adapter already. Import uses
|
|
41
|
+
# this when activerecord-import was required after the connection was
|
|
42
|
+
# established. Each adapter is only tried once, so an unsupported adapter
|
|
43
|
+
# doesn't search the load path on every import.
|
|
44
|
+
def self.load_from_connection_pool_once(connection_pool)
|
|
45
|
+
adapter = adapter_for(connection_pool)
|
|
46
|
+
ATTEMPTED_ADAPTERS.compute_if_absent(adapter) do
|
|
47
|
+
require_adapter adapter
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.adapter_for(connection_pool) # :nodoc:
|
|
53
|
+
connection_pool.db_config.adapter
|
|
54
|
+
end
|
|
55
|
+
private_class_method :adapter_for
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
require 'activerecord-import/import'
|
|
59
|
+
require 'activerecord-import/active_record/adapters/abstract_adapter'
|
|
60
|
+
require 'activerecord-import/synchronize'
|
|
61
|
+
require 'activerecord-import/value_sets_parser'
|
|
62
|
+
require 'activerecord-import/aliases'
|