activerecord-import 1.0.6 → 1.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 -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 
|
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
|