activerecord-import 1.0.6 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yaml +73 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +39 -6
- data/Gemfile +5 -2
- data/README.markdown +29 -16
- data/activerecord-import.gemspec +1 -1
- data/benchmarks/schema/{mysql_schema.rb → mysql2_schema.rb} +0 -0
- data/gemfiles/6.0.gemfile +1 -0
- data/gemfiles/6.1.gemfile +1 -1
- data/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb +4 -4
- data/lib/activerecord-import/adapters/mysql_adapter.rb +2 -2
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +1 -1
- data/lib/activerecord-import/import.rb +38 -15
- data/lib/activerecord-import/value_sets_parser.rb +2 -0
- data/lib/activerecord-import/version.rb +1 -1
- data/test/{travis → github}/database.yml +3 -1
- data/test/import_test.rb +38 -1
- data/test/models/card.rb +3 -0
- data/test/models/deck.rb +6 -0
- data/test/models/playing_card.rb +2 -0
- data/test/schema/generic_schema.rb +14 -0
- data/test/test_helper.rb +10 -1
- metadata +14 -8
- data/.travis.yml +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3360334cbd71089351c8211214bb4aedf0bd3e65305513b17c9fb09e3f1cf19
|
4
|
+
data.tar.gz: 2b97e3a1c6c2b39970dd3bb2a00a608d6e105cddac19d3f9ebaf28f8f30ece69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4585ba6ff2300d94fbd646d5f5a07cc254311cb9a47eacc7b1266eab8df5d9a53ca3018232e6c2517f05e1aef0dafaff20942bd1150ef518d688940f795bf2a9
|
7
|
+
data.tar.gz: 532506d134f067323d0490a913739089e55bf17893f602c752e5e963af2d7d799f5200a72cc48e7c780992e9f82c68273dc7af308034664ba898ed5292e199c4
|
@@ -0,0 +1,73 @@
|
|
1
|
+
name: Test
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
test:
|
5
|
+
services:
|
6
|
+
postgres:
|
7
|
+
image: postgis/postgis:10-2.5
|
8
|
+
env:
|
9
|
+
POSTGRES_USER: postgres
|
10
|
+
POSTGRES_PASSWORD: postgres
|
11
|
+
ports:
|
12
|
+
- 5432:5432
|
13
|
+
# Set health checks to wait until postgres has started
|
14
|
+
options: >-
|
15
|
+
--health-cmd pg_isready
|
16
|
+
--health-interval 10s
|
17
|
+
--health-timeout 5s
|
18
|
+
--health-retries 5
|
19
|
+
strategy:
|
20
|
+
fail-fast: false
|
21
|
+
matrix:
|
22
|
+
ruby:
|
23
|
+
- 2.6
|
24
|
+
env:
|
25
|
+
- AR_VERSION: 6.1
|
26
|
+
- AR_VERSION: 6.0
|
27
|
+
- AR_VERSION: 5.2
|
28
|
+
- AR_VERSION: 5.1
|
29
|
+
include:
|
30
|
+
- ruby: 2.4
|
31
|
+
env:
|
32
|
+
AR_VERSION: 5.0
|
33
|
+
- ruby: 2.4
|
34
|
+
env:
|
35
|
+
AR_VERSION: 4.2
|
36
|
+
runs-on: ubuntu-latest
|
37
|
+
env:
|
38
|
+
AR_VERSION: ${{ matrix.env.AR_VERSION }}
|
39
|
+
DB_DATABASE: activerecord_import_test
|
40
|
+
steps:
|
41
|
+
- uses: actions/checkout@v2
|
42
|
+
- uses: ruby/setup-ruby@v1
|
43
|
+
with:
|
44
|
+
ruby-version: ${{ matrix.ruby }}
|
45
|
+
- name: Setup Bundler 1.x for Ruby 2.3
|
46
|
+
if: ${{ matrix.ruby == '2.3' }}
|
47
|
+
run: echo "BUNDLER_VERSION=1.17.3" >> $GITHUB_ENV
|
48
|
+
- name: Set up databases
|
49
|
+
run: |
|
50
|
+
sudo /etc/init.d/mysql start
|
51
|
+
mysql -e 'CREATE DATABASE ${{ env.DB_DATABASE }} CHARACTER SET utf8 COLLATE utf8_general_ci;' -u root -proot
|
52
|
+
psql -h localhost -U postgres -c 'create database ${{ env.DB_DATABASE }};'
|
53
|
+
psql -h localhost -U postgres -d ${{ env.DB_DATABASE }} -c 'create extension if not exists hstore;'
|
54
|
+
psql -h localhost -U postgres -c 'create extension if not exists postgis;'
|
55
|
+
psql -h localhost -U postgres -c 'create extension if not exists "uuid-ossp";'
|
56
|
+
cp test/github/database.yml test/database.yml
|
57
|
+
env:
|
58
|
+
PGPASSWORD: postgres
|
59
|
+
- name: Install dependencies
|
60
|
+
run : AR_VERSION=${{ env.AR_VERSION }} bundle install
|
61
|
+
- name: Run tests
|
62
|
+
run: |
|
63
|
+
bundle exec rake test:mysql2
|
64
|
+
bundle exec rake test:mysql2_makara
|
65
|
+
bundle exec rake test:mysql2spatial
|
66
|
+
bundle exec rake test:postgis
|
67
|
+
bundle exec rake test:postgresql
|
68
|
+
bundle exec rake test:postgresql_makara
|
69
|
+
bundle exec rake test:seamless_database_pool
|
70
|
+
bundle exec rake test:spatialite
|
71
|
+
bundle exec rake test:sqlite3
|
72
|
+
- name: Run Rubocop
|
73
|
+
run: bundle exec rubocop
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,44 @@
|
|
1
|
+
## Changes in 1.2.0
|
2
|
+
|
3
|
+
### Fixes
|
4
|
+
|
5
|
+
* Update JDBC MySQL adapter to use mysql2 connection adapter. Thanks to @terencechow via \##744.
|
6
|
+
* Fix importing STI models with ActiveRecord 6. Thanks to @clemens1483 via \##743.
|
7
|
+
* Use polymorphic_name instead of base_class.name for imports. Thanks to @kmhajjar via \##741.
|
8
|
+
* Fix compatibility issue with composite primary keys. Thanks to @dlanileonardo via \##737.
|
9
|
+
* Prevent double validation of associations on recursive import.
|
10
|
+
|
11
|
+
## Changes in 1.1.0
|
12
|
+
|
13
|
+
### New Features
|
14
|
+
|
15
|
+
* Add batch progress reporting. Thanks to @gee-forr via \##729.
|
16
|
+
|
17
|
+
## Changes in 1.0.8
|
18
|
+
|
19
|
+
### Fixes
|
20
|
+
|
21
|
+
* Use correct method for clearing query cache. Thanks to @EtienneDepaulis via \##719.
|
22
|
+
|
23
|
+
## Changes in 1.0.7
|
24
|
+
|
25
|
+
### New Features
|
26
|
+
|
27
|
+
* Use @@max_allowed_packet session variable instead of querying SHOW VARIABLES. Thanks to @diclophis via \#706.
|
28
|
+
* Add option :track_validation_failures. When this is set to true, failed_instances will be an array of arrays, with each inner array having the form [:index_in_dataset, :object_with_errors]. Thanks to @rorymckinley via \#684.
|
29
|
+
|
30
|
+
### Fixes
|
31
|
+
|
32
|
+
* Prevent mass-assignment errors in Rails strict mode. Thanks to @diclophis via \##709.
|
33
|
+
|
1
34
|
## Changes in 1.0.6
|
2
35
|
|
3
36
|
### Fixes
|
4
37
|
|
5
38
|
* Handle after_initialize callbacks. Thanks to @AhMohsen46 via \#691 and
|
6
39
|
\#692.
|
7
|
-
* Fix regression introduced in 1.0.4.
|
8
|
-
support on duplicate key update. Thanks to @dsobiera, @jkowens via \#
|
40
|
+
* Fix regression introduced in 1.0.4. Explicitly allow adapters to
|
41
|
+
support on duplicate key update. Thanks to @dsobiera, @jkowens via \#696.
|
9
42
|
|
10
43
|
## Changes in 1.0.5
|
11
44
|
|
@@ -13,7 +46,7 @@
|
|
13
46
|
|
14
47
|
* Allow serialized attributes to be returned from import. Thanks to @timanovsky, @jkowens via \#660.
|
15
48
|
* Return ActiveRecord::Connection from
|
16
|
-
|
49
|
+
ActiveRecord::Base#establish_connection. Thanks to @reverentF via
|
17
50
|
\#663.
|
18
51
|
* Support PostgreSQL array. Thanks to @ujihisa via \#669.
|
19
52
|
* Skip loading association ids when column changed. Thanks to @Aristat
|
@@ -68,7 +101,7 @@
|
|
68
101
|
* Fix import issue for models with Postgresql json/jsonb fields. Thanks
|
69
102
|
to @stokarenko via \#594.
|
70
103
|
* Fix issue importing models with timestamps that contain timezone
|
71
|
-
information.
|
104
|
+
information. Thanks to @dekaikiwi, @jkowens via \#598.
|
72
105
|
* Ignore :no_returning when using :recursive option. Thanks to @dgollahon, @jkowens
|
73
106
|
via \#599.
|
74
107
|
|
@@ -283,7 +316,7 @@
|
|
283
316
|
Thanks to @jkowens via \#301.
|
284
317
|
* Allow for custom timestamp columns. Thanks to @mojidabckuu, @jkowens
|
285
318
|
via \#401.
|
286
|
-
|
319
|
+
|
287
320
|
### Fixes
|
288
321
|
|
289
322
|
* Fix ActiveRecord 5 issue coercing boolean values when serializing
|
@@ -295,7 +328,7 @@
|
|
295
328
|
|
296
329
|
* Fix issue where PostgreSQL cannot recognize columns if names
|
297
330
|
include mixed case characters. Thanks to @hugobgranja via \#379.
|
298
|
-
* Fix an issue for ActiveRecord 5 where serialized fields with
|
331
|
+
* Fix an issue for ActiveRecord 5 where serialized fields with
|
299
332
|
default values were not being typecast. Thanks to @whistlerbrk,
|
300
333
|
@jkowens via \#386.
|
301
334
|
* Add option :force_single_insert for MySQL to make sure a single
|
data/Gemfile
CHANGED
@@ -6,8 +6,11 @@ version = ENV['AR_VERSION'].to_f
|
|
6
6
|
|
7
7
|
mysql2_version = '0.3.0'
|
8
8
|
mysql2_version = '0.4.0' if version >= 4.2
|
9
|
+
mysql2_version = '0.5.0' if version >= 6.1
|
9
10
|
sqlite3_version = '1.3.0'
|
10
11
|
sqlite3_version = '1.4.0' if version >= 6.0
|
12
|
+
pg_version = '0.9'
|
13
|
+
pg_version = '1.1' if version >= 6.1
|
11
14
|
|
12
15
|
group :development, :test do
|
13
16
|
gem 'rubocop', '~> 0.40.0'
|
@@ -17,13 +20,13 @@ end
|
|
17
20
|
# Database Adapters
|
18
21
|
platforms :ruby do
|
19
22
|
gem "mysql2", "~> #{mysql2_version}"
|
20
|
-
gem "pg", "~>
|
23
|
+
gem "pg", "~> #{pg_version}"
|
21
24
|
gem "sqlite3", "~> #{sqlite3_version}"
|
22
25
|
gem "seamless_database_pool", "~> 1.0.20"
|
23
26
|
end
|
24
27
|
|
25
28
|
platforms :jruby do
|
26
|
-
gem
|
29
|
+
gem 'jdbc-mysql', '< 8', require: false
|
27
30
|
gem "jdbc-postgres"
|
28
31
|
gem "activerecord-jdbcsqlite3-adapter", "~> 1.3"
|
29
32
|
gem "activerecord-jdbcmysql-adapter", "~> 1.3"
|
data/README.markdown
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Activerecord-Import
|
1
|
+
# Activerecord-Import ![Build Status](https://github.com/zdennis/activerecord-import/actions/workflows/test.yaml/badge.svg)
|
2
2
|
|
3
3
|
Activerecord-Import is a library for bulk inserting data using ActiveRecord.
|
4
4
|
|
@@ -231,6 +231,18 @@ columns = [ :title ]
|
|
231
231
|
Book.import columns, books, batch_size: 2
|
232
232
|
```
|
233
233
|
|
234
|
+
If your import is particularly large or slow (possibly due to [callbacks](#callbacks)) whilst batch importing, you might want a way to report back on progress. This is supported by passing a callable as the `batch_progress` option. e.g:
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
my_proc = ->(rows_size, num_batches, current_batch_number, batch_duration_in_secs) {
|
238
|
+
# Using the arguments provided to the callable, you can
|
239
|
+
# send an email, post to a websocket,
|
240
|
+
# update slack, alert if import is taking too long, etc.
|
241
|
+
}
|
242
|
+
|
243
|
+
Book.import columns, books, batch_size: 2, batch_progress: my_proc
|
244
|
+
```
|
245
|
+
|
234
246
|
#### Recursive
|
235
247
|
|
236
248
|
NOTE: This only works with PostgreSQL and ActiveRecord objects. This won't work with
|
@@ -250,20 +262,21 @@ Book.import books, recursive: true
|
|
250
262
|
|
251
263
|
### Options
|
252
264
|
|
253
|
-
Key
|
254
|
-
|
255
|
-
:validate
|
256
|
-
:validate_uniqueness
|
257
|
-
:validate_with_context
|
258
|
-
:
|
259
|
-
:
|
260
|
-
:
|
261
|
-
:
|
262
|
-
:
|
263
|
-
:
|
264
|
-
:
|
265
|
-
:
|
266
|
-
:
|
265
|
+
Key | Options | Default | Description
|
266
|
+
------------------------- | --------------------- | ------------------ | -----------
|
267
|
+
:validate | `true`/`false` | `true` | Whether or not to run `ActiveRecord` validations (uniqueness skipped). This option will always be true when using `import!`.
|
268
|
+
:validate_uniqueness | `true`/`false` | `false` | Whether or not to run uniqueness validations, has potential pitfalls, use with caution (requires `>= v0.27.0`).
|
269
|
+
:validate_with_context | `Symbol` |`:create`/`:update` | Allows passing an ActiveModel validation context for each model. Default is `:create` for new records and `:update` for existing ones.
|
270
|
+
:track_validation_failures| `true`/`false` | `false` | When this is set to true, `failed_instances` will be an array of arrays, with each inner array having the form `[:index_in_dataset, :object_with_errors]`
|
271
|
+
:on_duplicate_key_ignore | `true`/`false` | `false` | Allows skipping records with duplicate keys. See [here](https://github.com/zdennis/activerecord-import/#duplicate-key-ignore) for more details.
|
272
|
+
:ignore | `true`/`false` | `false` | Alias for :on_duplicate_key_ignore.
|
273
|
+
:on_duplicate_key_update | :all, `Array`, `Hash` | N/A | Allows upsert logic to be used. See [here](https://github.com/zdennis/activerecord-import/#duplicate-key-update) for more details.
|
274
|
+
:synchronize | `Array` | N/A | An array of ActiveRecord instances. This synchronizes existing instances in memory with updates from the import.
|
275
|
+
:timestamps | `true`/`false` | `true` | Enables/disables timestamps on imported records.
|
276
|
+
:recursive | `true`/`false` | `false` | Imports has_many/has_one associations (PostgreSQL only).
|
277
|
+
:batch_size | `Integer` | total # of records | Max number of records to insert per import
|
278
|
+
:raise_error | `true`/`false` | `false` | Raises an exception at the first invalid record. This means there will not be a result object returned. The `import!` method is a shortcut for this.
|
279
|
+
:all_or_none | `true`/`false` | `false` | Will not import any records if there is a record with validation errors.
|
267
280
|
|
268
281
|
#### Duplicate Key Ignore
|
269
282
|
|
@@ -379,7 +392,7 @@ Book.import books, validate_uniqueness: true
|
|
379
392
|
|
380
393
|
### Return Info
|
381
394
|
|
382
|
-
The `import` method returns a `Result` object that responds to `failed_instances` and `num_inserts`. Additionally, for users of Postgres, there will be two arrays `ids` and `results` that can be accessed
|
395
|
+
The `import` method returns a `Result` object that responds to `failed_instances` and `num_inserts`. Additionally, for users of Postgres, there will be two arrays `ids` and `results` that can be accessed.
|
383
396
|
|
384
397
|
```ruby
|
385
398
|
articles = [
|
data/activerecord-import.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
|
|
6
6
|
gem.email = ["zach.dennis@gmail.com"]
|
7
7
|
gem.summary = "Bulk insert extension for ActiveRecord"
|
8
8
|
gem.description = "A library for bulk inserting data using ActiveRecord."
|
9
|
-
gem.homepage = "
|
9
|
+
gem.homepage = "https://github.com/zdennis/activerecord-import"
|
10
10
|
gem.license = "MIT"
|
11
11
|
|
12
12
|
gem.files = `git ls-files`.split($\)
|
File without changes
|
data/gemfiles/6.0.gemfile
CHANGED
data/gemfiles/6.1.gemfile
CHANGED
@@ -1 +1 @@
|
|
1
|
-
gem 'activerecord', '~> 6.1.0
|
1
|
+
gem 'activerecord', '~> 6.1.0'
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require "active_record/connection_adapters/
|
2
|
-
require "activerecord-import/adapters/
|
1
|
+
require "active_record/connection_adapters/mysql2_adapter"
|
2
|
+
require "activerecord-import/adapters/mysql2_adapter"
|
3
3
|
|
4
|
-
class ActiveRecord::ConnectionAdapters::
|
5
|
-
include ActiveRecord::Import::
|
4
|
+
class ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
5
|
+
include ActiveRecord::Import::Mysql2Adapter
|
6
6
|
end
|
@@ -56,9 +56,9 @@ module ActiveRecord::Import::MysqlAdapter
|
|
56
56
|
# in a single packet
|
57
57
|
def max_allowed_packet # :nodoc:
|
58
58
|
@max_allowed_packet ||= begin
|
59
|
-
result = execute( "
|
59
|
+
result = execute( "SELECT @@max_allowed_packet" )
|
60
60
|
# original Mysql gem responds to #fetch_row while Mysql2 responds to #first
|
61
|
-
val = result.respond_to?(:fetch_row) ? result.fetch_row[
|
61
|
+
val = result.respond_to?(:fetch_row) ? result.fetch_row[0] : result.first[0]
|
62
62
|
val.to_i
|
63
63
|
end
|
64
64
|
end
|
@@ -49,7 +49,7 @@ module ActiveRecord::Import #:nodoc:
|
|
49
49
|
associations = klass.reflect_on_all_associations(:belongs_to)
|
50
50
|
associations.each do |assoc|
|
51
51
|
if (index = attrs.index(assoc.name))
|
52
|
-
key = assoc.foreign_key.to_sym
|
52
|
+
key = assoc.foreign_key.is_a?(Array) ? assoc.foreign_key.map(&:to_sym) : assoc.foreign_key.to_sym
|
53
53
|
attrs[index] = key unless attrs.include?(key)
|
54
54
|
end
|
55
55
|
end
|
@@ -547,7 +547,7 @@ class ActiveRecord::Base
|
|
547
547
|
alias import! bulk_import! unless ActiveRecord::Base.respond_to? :import!
|
548
548
|
|
549
549
|
def import_helper( *args )
|
550
|
-
options = { validate: true, timestamps: true }
|
550
|
+
options = { validate: true, timestamps: true, track_validation_failures: false }
|
551
551
|
options.merge!( args.pop ) if args.last.is_a? Hash
|
552
552
|
# making sure that current model's primary key is used
|
553
553
|
options[:primary_key] = primary_key
|
@@ -703,13 +703,18 @@ class ActiveRecord::Base
|
|
703
703
|
# keep track of the instance and the position it is currently at. if this fails
|
704
704
|
# validation we'll use the index to remove it from the array_of_attributes
|
705
705
|
arr.each_with_index do |hsh, i|
|
706
|
-
|
706
|
+
# utilize block initializer syntax to prevent failure when 'mass_assignment_sanitizer = :strict'
|
707
|
+
model = new do |m|
|
708
|
+
hsh.each_pair { |k, v| m[k] = v }
|
709
|
+
end
|
710
|
+
|
707
711
|
next if validator.valid_model?(model)
|
708
712
|
raise(ActiveRecord::RecordInvalid, model) if options[:raise_error]
|
713
|
+
|
709
714
|
array_of_attributes[i] = nil
|
710
715
|
failure = model.dup
|
711
716
|
failure.errors.send(:initialize_dup, model.errors)
|
712
|
-
failed_instances << failure
|
717
|
+
failed_instances << (options[:track_validation_failures] ? [i, failure] : failure )
|
713
718
|
end
|
714
719
|
array_of_attributes.compact!
|
715
720
|
end
|
@@ -729,7 +734,7 @@ class ActiveRecord::Base
|
|
729
734
|
set_attributes_and_mark_clean(models, return_obj, timestamps, options)
|
730
735
|
|
731
736
|
# if there are auto-save associations on the models we imported that are new, import them as well
|
732
|
-
import_associations(models, options.dup) if options[:recursive]
|
737
|
+
import_associations(models, options.dup.merge(validate: false)) if options[:recursive]
|
733
738
|
end
|
734
739
|
|
735
740
|
return_obj
|
@@ -770,21 +775,22 @@ class ActiveRecord::Base
|
|
770
775
|
unless scope_columns.blank?
|
771
776
|
scope_columns.zip(scope_values).each do |name, value|
|
772
777
|
name_as_sym = name.to_sym
|
773
|
-
next if column_names.include?(name_as_sym)
|
774
|
-
|
775
|
-
is_sti = (name_as_sym == inheritance_column.to_sym && self < base_class)
|
776
|
-
value = Array(value).first if is_sti
|
777
|
-
|
778
|
+
next if column_names.include?(name_as_sym) || name_as_sym == inheritance_column.to_sym
|
778
779
|
column_names << name_as_sym
|
779
780
|
array_of_attributes.each { |attrs| attrs << value }
|
780
781
|
end
|
781
782
|
end
|
782
783
|
|
784
|
+
if finder_needs_type_condition?
|
785
|
+
unless column_names.include?(inheritance_column.to_sym)
|
786
|
+
column_names << inheritance_column.to_sym
|
787
|
+
array_of_attributes.each { |attrs| attrs << sti_name }
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
783
791
|
columns = column_names.each_with_index.map do |name, i|
|
784
792
|
column = columns_hash[name.to_s]
|
785
|
-
|
786
793
|
raise ActiveRecord::Import::MissingColumnError.new(name.to_s, i) if column.nil?
|
787
|
-
|
788
794
|
column
|
789
795
|
end
|
790
796
|
|
@@ -800,17 +806,29 @@ class ActiveRecord::Base
|
|
800
806
|
if supports_import?
|
801
807
|
# generate the sql
|
802
808
|
post_sql_statements = connection.post_sql_statements( quoted_table_name, options )
|
809
|
+
import_size = values_sql.size
|
810
|
+
|
811
|
+
batch_size = options[:batch_size] || import_size
|
812
|
+
run_proc = options[:batch_size].to_i.positive? && options[:batch_progress].respond_to?( :call )
|
813
|
+
progress_proc = options[:batch_progress]
|
814
|
+
current_batch = 0
|
815
|
+
batches = (import_size / batch_size.to_f).ceil
|
803
816
|
|
804
|
-
batch_size = options[:batch_size] || values_sql.size
|
805
817
|
values_sql.each_slice(batch_size) do |batch_values|
|
818
|
+
batch_started_at = Time.now.to_i
|
819
|
+
|
806
820
|
# perform the inserts
|
807
821
|
result = connection.insert_many( [insert_sql, post_sql_statements].flatten,
|
808
822
|
batch_values,
|
809
823
|
options,
|
810
|
-
"#{model_name} Create Many
|
824
|
+
"#{model_name} Create Many" )
|
825
|
+
|
811
826
|
number_inserted += result.num_inserts
|
812
827
|
ids += result.ids
|
813
828
|
results += result.results
|
829
|
+
current_batch += 1
|
830
|
+
|
831
|
+
progress_proc.call(import_size, batches, current_batch, Time.now.to_i - batch_started_at) if run_proc
|
814
832
|
end
|
815
833
|
else
|
816
834
|
transaction(requires_new: true) do
|
@@ -944,8 +962,13 @@ class ActiveRecord::Base
|
|
944
962
|
changed_objects.each do |child|
|
945
963
|
child.public_send("#{association_reflection.foreign_key}=", model.id)
|
946
964
|
# For polymorphic associations
|
965
|
+
association_name = if model.class.respond_to?(:polymorphic_name)
|
966
|
+
model.class.polymorphic_name
|
967
|
+
else
|
968
|
+
model.class.base_class
|
969
|
+
end
|
947
970
|
association_reflection.type.try do |type|
|
948
|
-
child.public_send("#{type}=",
|
971
|
+
child.public_send("#{type}=", association_name)
|
949
972
|
end
|
950
973
|
end
|
951
974
|
associated_objects_by_class[model.class.name][association_reflection.name].concat changed_objects
|
@@ -1,7 +1,8 @@
|
|
1
1
|
common: &common
|
2
2
|
username: root
|
3
|
-
password:
|
3
|
+
password: root
|
4
4
|
encoding: utf8
|
5
|
+
collation: utf8_general_ci
|
5
6
|
host: localhost
|
6
7
|
database: activerecord_import_test
|
7
8
|
|
@@ -37,6 +38,7 @@ oracle:
|
|
37
38
|
postgresql: &postgresql
|
38
39
|
<<: *common
|
39
40
|
username: postgres
|
41
|
+
password: postgres
|
40
42
|
adapter: postgresql
|
41
43
|
min_messages: warning
|
42
44
|
|
data/test/import_test.rb
CHANGED
@@ -169,7 +169,17 @@ describe "#import" do
|
|
169
169
|
assert_difference "Dictionary.count", +1 do
|
170
170
|
Dictionary.import dictionaries
|
171
171
|
end
|
172
|
-
assert_equal "Dictionary", Dictionary.
|
172
|
+
assert_equal "Dictionary", Dictionary.last.type
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should import arrays successfully" do
|
176
|
+
columns = [:author_name, :title]
|
177
|
+
values = [["Noah Webster", "Webster's Dictionary"]]
|
178
|
+
|
179
|
+
assert_difference "Dictionary.count", +1 do
|
180
|
+
Dictionary.import columns, values
|
181
|
+
end
|
182
|
+
assert_equal "Dictionary", Dictionary.last.type
|
173
183
|
end
|
174
184
|
end
|
175
185
|
|
@@ -252,6 +262,16 @@ describe "#import" do
|
|
252
262
|
end
|
253
263
|
end
|
254
264
|
|
265
|
+
it "should index the failed instances by their poistion in the set if `track_failures` is true" do
|
266
|
+
index_offset = valid_values.length
|
267
|
+
results = Topic.import columns, valid_values + invalid_values, validate: true, track_validation_failures: true
|
268
|
+
assert_equal invalid_values.size, results.failed_instances.size
|
269
|
+
invalid_values.each_with_index do |value_set, index|
|
270
|
+
assert_equal index + index_offset, results.failed_instances[index].first
|
271
|
+
assert_equal value_set.first, results.failed_instances[index].last.title
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
255
275
|
it "should set ids in valid models if adapter supports setting primary key of imported objects" do
|
256
276
|
if ActiveRecord::Base.supports_setting_primary_key_of_imported_objects?
|
257
277
|
Topic.import (invalid_models + valid_models), validate: true
|
@@ -395,6 +415,15 @@ describe "#import" do
|
|
395
415
|
assert_equal 3, result.num_inserts if Topic.supports_import?
|
396
416
|
end
|
397
417
|
end
|
418
|
+
|
419
|
+
it "should accept and call an optional callable to run after each batch" do
|
420
|
+
lambda_called = 0
|
421
|
+
|
422
|
+
my_proc = ->(_row_count, _batches, _batch, _duration) { lambda_called += 1 }
|
423
|
+
Topic.import Build(10, :topics), batch_size: 4, batch_progress: my_proc
|
424
|
+
|
425
|
+
assert_equal 3, lambda_called
|
426
|
+
end
|
398
427
|
end
|
399
428
|
|
400
429
|
context "with :synchronize option" do
|
@@ -642,6 +671,14 @@ describe "#import" do
|
|
642
671
|
assert_equal [val1, val2], scope.map(&column).sort
|
643
672
|
end
|
644
673
|
|
674
|
+
context "for cards and decks" do
|
675
|
+
it "works when the polymorphic name is different than base class name" do
|
676
|
+
deck = Deck.create(id: 1, name: 'test')
|
677
|
+
deck.cards.import [:id, :deck_type], [[1, 'PlayingCard']]
|
678
|
+
assert_equal deck.cards.first.deck_type, "PlayingCard"
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
645
682
|
it "works importing array of hashes" do
|
646
683
|
scope.import [{ column => val1 }, { column => val2 }]
|
647
684
|
|
data/test/models/card.rb
ADDED
data/test/models/deck.rb
ADDED
@@ -52,6 +52,20 @@ ActiveRecord::Schema.define do
|
|
52
52
|
t.string :name
|
53
53
|
end
|
54
54
|
|
55
|
+
create_table :cards, force: :cascade do |t|
|
56
|
+
t.string :name
|
57
|
+
t.string :deck_type
|
58
|
+
t.integer :deck_id
|
59
|
+
end
|
60
|
+
|
61
|
+
create_table :decks, force: :cascade do |t|
|
62
|
+
t.string :name
|
63
|
+
end
|
64
|
+
|
65
|
+
create_table :playing_cards, force: :cascade do |t|
|
66
|
+
t.string :name
|
67
|
+
end
|
68
|
+
|
55
69
|
create_table :books, force: :cascade do |t|
|
56
70
|
t.string :title, null: false
|
57
71
|
t.string :publisher, null: false, default: 'Default Publisher'
|
data/test/test_helper.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pathname'
|
2
|
+
require 'rake'
|
2
3
|
test_dir = Pathname.new File.dirname(__FILE__)
|
3
4
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
@@ -48,7 +49,15 @@ adapter = ENV["ARE_DB"] || "sqlite3"
|
|
48
49
|
FileUtils.mkdir_p 'log'
|
49
50
|
ActiveRecord::Base.logger = Logger.new("log/test.log")
|
50
51
|
ActiveRecord::Base.logger.level = Logger::DEBUG
|
51
|
-
|
52
|
+
|
53
|
+
if ENV['AR_VERSION'].to_f >= 6.0
|
54
|
+
yaml_config = YAML.load_file(test_dir.join("database.yml"))[adapter]
|
55
|
+
config = ActiveRecord::DatabaseConfigurations::HashConfig.new("test", adapter, yaml_config)
|
56
|
+
ActiveRecord::Base.configurations.configurations << config
|
57
|
+
else
|
58
|
+
ActiveRecord::Base.configurations["test"] = YAML.load_file(test_dir.join("database.yml"))[adapter]
|
59
|
+
end
|
60
|
+
|
52
61
|
ActiveRecord::Base.default_timezone = :utc
|
53
62
|
|
54
63
|
require "activerecord-import"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-import
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Dennis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -45,10 +45,10 @@ executables: []
|
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
|
+
- ".github/workflows/test.yaml"
|
48
49
|
- ".gitignore"
|
49
50
|
- ".rubocop.yml"
|
50
51
|
- ".rubocop_todo.yml"
|
51
|
-
- ".travis.yml"
|
52
52
|
- Brewfile
|
53
53
|
- CHANGELOG.md
|
54
54
|
- Gemfile
|
@@ -67,7 +67,7 @@ files:
|
|
67
67
|
- benchmarks/models/test_innodb.rb
|
68
68
|
- benchmarks/models/test_memory.rb
|
69
69
|
- benchmarks/models/test_myisam.rb
|
70
|
-
- benchmarks/schema/
|
70
|
+
- benchmarks/schema/mysql2_schema.rb
|
71
71
|
- gemfiles/3.2.gemfile
|
72
72
|
- gemfiles/4.0.gemfile
|
73
73
|
- gemfiles/4.1.gemfile
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- test/adapters/spatialite.rb
|
115
115
|
- test/adapters/sqlite3.rb
|
116
116
|
- test/database.yml.sample
|
117
|
+
- test/github/database.yml
|
117
118
|
- test/import_test.rb
|
118
119
|
- test/jdbcmysql/import_test.rb
|
119
120
|
- test/jdbcpostgresql/import_test.rb
|
@@ -125,11 +126,14 @@ files:
|
|
125
126
|
- test/models/bike_maker.rb
|
126
127
|
- test/models/book.rb
|
127
128
|
- test/models/car.rb
|
129
|
+
- test/models/card.rb
|
128
130
|
- test/models/chapter.rb
|
131
|
+
- test/models/deck.rb
|
129
132
|
- test/models/dictionary.rb
|
130
133
|
- test/models/discount.rb
|
131
134
|
- test/models/end_note.rb
|
132
135
|
- test/models/group.rb
|
136
|
+
- test/models/playing_card.rb
|
133
137
|
- test/models/promotion.rb
|
134
138
|
- test/models/question.rb
|
135
139
|
- test/models/rule.rb
|
@@ -164,10 +168,9 @@ files:
|
|
164
168
|
- test/support/sqlite3/import_examples.rb
|
165
169
|
- test/synchronize_test.rb
|
166
170
|
- test/test_helper.rb
|
167
|
-
- test/travis/database.yml
|
168
171
|
- test/value_sets_bytes_parser_test.rb
|
169
172
|
- test/value_sets_records_parser_test.rb
|
170
|
-
homepage:
|
173
|
+
homepage: https://github.com/zdennis/activerecord-import
|
171
174
|
licenses:
|
172
175
|
- MIT
|
173
176
|
metadata: {}
|
@@ -186,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
189
|
- !ruby/object:Gem::Version
|
187
190
|
version: '0'
|
188
191
|
requirements: []
|
189
|
-
rubygems_version: 3.0.
|
192
|
+
rubygems_version: 3.0.9
|
190
193
|
signing_key:
|
191
194
|
specification_version: 4
|
192
195
|
summary: Bulk insert extension for ActiveRecord
|
@@ -205,6 +208,7 @@ test_files:
|
|
205
208
|
- test/adapters/spatialite.rb
|
206
209
|
- test/adapters/sqlite3.rb
|
207
210
|
- test/database.yml.sample
|
211
|
+
- test/github/database.yml
|
208
212
|
- test/import_test.rb
|
209
213
|
- test/jdbcmysql/import_test.rb
|
210
214
|
- test/jdbcpostgresql/import_test.rb
|
@@ -216,11 +220,14 @@ test_files:
|
|
216
220
|
- test/models/bike_maker.rb
|
217
221
|
- test/models/book.rb
|
218
222
|
- test/models/car.rb
|
223
|
+
- test/models/card.rb
|
219
224
|
- test/models/chapter.rb
|
225
|
+
- test/models/deck.rb
|
220
226
|
- test/models/dictionary.rb
|
221
227
|
- test/models/discount.rb
|
222
228
|
- test/models/end_note.rb
|
223
229
|
- test/models/group.rb
|
230
|
+
- test/models/playing_card.rb
|
224
231
|
- test/models/promotion.rb
|
225
232
|
- test/models/question.rb
|
226
233
|
- test/models/rule.rb
|
@@ -255,6 +262,5 @@ test_files:
|
|
255
262
|
- test/support/sqlite3/import_examples.rb
|
256
263
|
- test/synchronize_test.rb
|
257
264
|
- test/test_helper.rb
|
258
|
-
- test/travis/database.yml
|
259
265
|
- test/value_sets_bytes_parser_test.rb
|
260
266
|
- test/value_sets_records_parser_test.rb
|
data/.travis.yml
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
cache: bundler
|
3
|
-
rvm:
|
4
|
-
- 2.5.5
|
5
|
-
|
6
|
-
env:
|
7
|
-
global:
|
8
|
-
# https://github.com/discourse/discourse/blob/master/.travis.yml
|
9
|
-
- RUBY_GC_MALLOC_LIMIT=50000000
|
10
|
-
matrix:
|
11
|
-
- AR_VERSION=5.1
|
12
|
-
- AR_VERSION=5.2
|
13
|
-
- AR_VERSION=6.0
|
14
|
-
|
15
|
-
matrix:
|
16
|
-
include:
|
17
|
-
- rvm: 2.3.8
|
18
|
-
env: AR_VERSION=3.2
|
19
|
-
- rvm: 2.3.8
|
20
|
-
env: AR_VERSION=4.0
|
21
|
-
- rvm: 2.3.8
|
22
|
-
env: AR_VERSION=4.1
|
23
|
-
- rvm: 2.3.8
|
24
|
-
env: AR_VERSION=4.2
|
25
|
-
- rvm: 2.3.8
|
26
|
-
env: AR_VERSION=5.0
|
27
|
-
|
28
|
-
fast_finish: true
|
29
|
-
|
30
|
-
addons:
|
31
|
-
postgresql: "9.5"
|
32
|
-
apt:
|
33
|
-
sources:
|
34
|
-
- travis-ci/sqlite3
|
35
|
-
- mysql-5.7-trusty
|
36
|
-
packages:
|
37
|
-
- sqlite3
|
38
|
-
- mysql-server
|
39
|
-
- mysql-client
|
40
|
-
- postgresql-9.5-postgis-2.4
|
41
|
-
|
42
|
-
before_install:
|
43
|
-
- gem update --system
|
44
|
-
- sudo mysql -e "use mysql; update user set authentication_string=PASSWORD('') where User='root'; update user set plugin='mysql_native_password';FLUSH PRIVILEGES;"
|
45
|
-
- sudo mysql_upgrade
|
46
|
-
- sudo service mysql restart
|
47
|
-
|
48
|
-
before_script:
|
49
|
-
- mysql -e 'create database activerecord_import_test;'
|
50
|
-
- psql -c 'create database activerecord_import_test;' -U postgres
|
51
|
-
- psql activerecord_import_test -c 'create extension if not exists hstore;' -U postgres
|
52
|
-
- psql -c 'create extension if not exists postgis;' -U postgres
|
53
|
-
- psql -c 'create extension if not exists "uuid-ossp";' -U postgres
|
54
|
-
- cp test/travis/database.yml test/database.yml
|
55
|
-
|
56
|
-
script:
|
57
|
-
- bundle exec rake test:mysql2
|
58
|
-
- bundle exec rake test:mysql2_makara
|
59
|
-
- bundle exec rake test:mysql2spatial
|
60
|
-
- bundle exec rake test:postgis
|
61
|
-
- bundle exec rake test:postgresql
|
62
|
-
- bundle exec rake test:postgresql_makara
|
63
|
-
- bundle exec rake test:seamless_database_pool
|
64
|
-
- bundle exec rake test:spatialite
|
65
|
-
- bundle exec rake test:sqlite3
|
66
|
-
- bundle exec rubocop
|
67
|
-
|
68
|
-
dist: xenial
|
69
|
-
|
70
|
-
services:
|
71
|
-
- mysql
|
72
|
-
- postgresql
|
73
|
-
|
74
|
-
sudo: required
|