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.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +124 -0
  3. data/README.markdown +99 -32
  4. data/lib/activerecord-import/active_record/adapters/mysql2_proxy_adapter.rb +8 -0
  5. data/lib/activerecord-import/active_record/adapters/postgresql_proxy_adapter.rb +8 -0
  6. data/lib/activerecord-import/active_record/adapters/sqlite3_proxy_adapter.rb +8 -0
  7. data/lib/activerecord-import/active_record/adapters/trilogy_proxy_adapter.rb +8 -0
  8. data/lib/activerecord-import/adapters/abstract_adapter.rb +58 -0
  9. data/lib/activerecord-import/adapters/active_record_proxy_adapter.rb +19 -0
  10. data/lib/activerecord-import/adapters/mysql2_proxy_adapter.rb +9 -0
  11. data/lib/activerecord-import/adapters/mysql_adapter.rb +70 -13
  12. data/lib/activerecord-import/adapters/postgresql_adapter.rb +10 -52
  13. data/lib/activerecord-import/adapters/postgresql_proxy_adapter.rb +9 -0
  14. data/lib/activerecord-import/adapters/sqlite3_adapter.rb +7 -3
  15. data/lib/activerecord-import/adapters/sqlite3_proxy_adapter.rb +9 -0
  16. data/lib/activerecord-import/adapters/trilogy_proxy_adapter.rb +9 -0
  17. data/lib/activerecord-import/aliases.rb +42 -0
  18. data/lib/activerecord-import/base.rb +2 -46
  19. data/lib/activerecord-import/core.rb +62 -0
  20. data/lib/activerecord-import/import.rb +612 -369
  21. data/lib/activerecord-import/synchronize.rb +28 -16
  22. data/lib/activerecord-import/version.rb +1 -1
  23. data/lib/activerecord-import/without_aliases.rb +17 -0
  24. metadata +19 -211
  25. data/.github/workflows/test.yaml +0 -165
  26. data/.gitignore +0 -37
  27. data/.rubocop.yml +0 -118
  28. data/.rubocop_todo.yml +0 -30
  29. data/Brewfile +0 -5
  30. data/Dockerfile +0 -23
  31. data/Gemfile +0 -63
  32. data/Rakefile +0 -75
  33. data/activerecord-import.gemspec +0 -28
  34. data/benchmarks/README +0 -32
  35. data/benchmarks/benchmark.rb +0 -72
  36. data/benchmarks/lib/base.rb +0 -143
  37. data/benchmarks/lib/cli_parser.rb +0 -111
  38. data/benchmarks/lib/float.rb +0 -17
  39. data/benchmarks/lib/mysql2_benchmark.rb +0 -21
  40. data/benchmarks/lib/output_to_csv.rb +0 -21
  41. data/benchmarks/lib/output_to_html.rb +0 -66
  42. data/benchmarks/models/test_innodb.rb +0 -5
  43. data/benchmarks/models/test_memory.rb +0 -5
  44. data/benchmarks/models/test_myisam.rb +0 -5
  45. data/benchmarks/schema/mysql2_schema.rb +0 -18
  46. data/docker-compose.yml +0 -34
  47. data/gemfiles/5.2.gemfile +0 -4
  48. data/gemfiles/6.0.gemfile +0 -4
  49. data/gemfiles/6.1.gemfile +0 -4
  50. data/gemfiles/7.0.gemfile +0 -4
  51. data/gemfiles/7.1.gemfile +0 -3
  52. data/gemfiles/7.2.gemfile +0 -3
  53. data/gemfiles/8.0.gemfile +0 -3
  54. data/test/adapters/janus_mysql2.rb +0 -3
  55. data/test/adapters/janus_trilogy.rb +0 -11
  56. data/test/adapters/jdbcmysql.rb +0 -3
  57. data/test/adapters/jdbcpostgresql.rb +0 -3
  58. data/test/adapters/jdbcsqlite3.rb +0 -3
  59. data/test/adapters/makara_postgis.rb +0 -3
  60. data/test/adapters/mysql2.rb +0 -3
  61. data/test/adapters/mysql2_makara.rb +0 -3
  62. data/test/adapters/mysql2_proxy.rb +0 -3
  63. data/test/adapters/mysql2spatial.rb +0 -3
  64. data/test/adapters/postgis.rb +0 -3
  65. data/test/adapters/postgresql.rb +0 -3
  66. data/test/adapters/postgresql_makara.rb +0 -3
  67. data/test/adapters/postgresql_proxy.rb +0 -3
  68. data/test/adapters/seamless_database_pool.rb +0 -3
  69. data/test/adapters/spatialite.rb +0 -3
  70. data/test/adapters/sqlite3.rb +0 -3
  71. data/test/adapters/trilogy.rb +0 -11
  72. data/test/database.yml.sample +0 -73
  73. data/test/github/database.yml +0 -85
  74. data/test/import_test.rb +0 -970
  75. data/test/janus_mysql2/import_test.rb +0 -8
  76. data/test/janus_trilogy/import_test.rb +0 -7
  77. data/test/jdbcmysql/import_test.rb +0 -7
  78. data/test/jdbcpostgresql/import_test.rb +0 -6
  79. data/test/jdbcsqlite3/import_test.rb +0 -6
  80. data/test/makara_postgis/import_test.rb +0 -10
  81. data/test/models/account.rb +0 -5
  82. data/test/models/alarm.rb +0 -4
  83. data/test/models/animal.rb +0 -8
  84. data/test/models/author.rb +0 -9
  85. data/test/models/bike_maker.rb +0 -10
  86. data/test/models/book.rb +0 -18
  87. data/test/models/car.rb +0 -5
  88. data/test/models/card.rb +0 -5
  89. data/test/models/chapter.rb +0 -6
  90. data/test/models/composite_book.rb +0 -19
  91. data/test/models/composite_chapter.rb +0 -12
  92. data/test/models/customer.rb +0 -18
  93. data/test/models/deck.rb +0 -8
  94. data/test/models/dictionary.rb +0 -6
  95. data/test/models/discount.rb +0 -5
  96. data/test/models/end_note.rb +0 -6
  97. data/test/models/group.rb +0 -5
  98. data/test/models/order.rb +0 -17
  99. data/test/models/playing_card.rb +0 -4
  100. data/test/models/promotion.rb +0 -5
  101. data/test/models/question.rb +0 -5
  102. data/test/models/rule.rb +0 -5
  103. data/test/models/tag.rb +0 -12
  104. data/test/models/tag_alias.rb +0 -11
  105. data/test/models/topic.rb +0 -31
  106. data/test/models/user.rb +0 -5
  107. data/test/models/user_token.rb +0 -6
  108. data/test/models/vendor.rb +0 -9
  109. data/test/models/widget.rb +0 -33
  110. data/test/mysql2/import_test.rb +0 -7
  111. data/test/mysql2_makara/import_test.rb +0 -8
  112. data/test/mysql2_proxy/import_test.rb +0 -6
  113. data/test/mysqlspatial2/import_test.rb +0 -8
  114. data/test/postgis/import_test.rb +0 -10
  115. data/test/postgresql/import_test.rb +0 -6
  116. data/test/postgresql_proxy/import_test.rb +0 -6
  117. data/test/schema/generic_schema.rb +0 -230
  118. data/test/schema/jdbcpostgresql_schema.rb +0 -3
  119. data/test/schema/mysql2_schema.rb +0 -21
  120. data/test/schema/postgis_schema.rb +0 -3
  121. data/test/schema/postgresql_schema.rb +0 -94
  122. data/test/schema/sqlite3_schema.rb +0 -15
  123. data/test/schema/version.rb +0 -12
  124. data/test/sqlite3/import_test.rb +0 -6
  125. data/test/support/active_support/test_case_extensions.rb +0 -73
  126. data/test/support/assertions.rb +0 -75
  127. data/test/support/factories.rb +0 -66
  128. data/test/support/generate.rb +0 -31
  129. data/test/support/mysql/import_examples.rb +0 -97
  130. data/test/support/postgresql/import_examples.rb +0 -642
  131. data/test/support/shared_examples/on_duplicate_key_ignore.rb +0 -45
  132. data/test/support/shared_examples/on_duplicate_key_update.rb +0 -437
  133. data/test/support/shared_examples/recursive_import.rb +0 -352
  134. data/test/support/sqlite3/import_examples.rb +0 -232
  135. data/test/synchronize_test.rb +0 -43
  136. data/test/test_helper.rb +0 -91
  137. data/test/trilogy/import_test.rb +0 -7
  138. data/test/value_sets_bytes_parser_test.rb +0 -106
  139. 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
- number_of_inserts += 1
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 do |value_set|
48
- number_of_inserts += 1
49
- sql2insert = base_sql + value_set.join( ',' ) + post_sql
50
- insert( sql2insert, *args )
51
- end
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
- sql << "IGNORE" if options[:ignore] || options[:on_duplicate_key_ignore]
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.attribute_alias?( column ) ? model.attribute_alias( column ) : column
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.attribute_alias?( column1 ) ? model.attribute_alias( column1 ) : column1
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.attribute_alias?( column2 ) ? model.attribute_alias( column2 ) : column2
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( sql2insert, *args )
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
- if options[:returning].blank?
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.attribute_alias?( column ) ? model.attribute_alias( column ) : column
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.attribute_alias?( column1 ) ? model.attribute_alias( column1 ) : column1
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.attribute_alias?( column2 ) ? model.attribute_alias( column2 ) : column2
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.attribute_alias?( column ) ? model.attribute_alias( column ) : column
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.attribute_alias?( column1 ) ? model.attribute_alias( column1 ) : column1
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.attribute_alias?( column2 ) ? model.attribute_alias( column2 ) : column2
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 "pathname"
4
- require "active_record"
5
- require "active_record/version"
3
+ require "activerecord-import/core"
6
4
 
7
- module ActiveRecord::Import
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'