activerecord-import 1.4.1 → 1.8.1
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 +53 -13
- data/.gitignore +4 -0
- data/.rubocop.yml +7 -4
- data/.rubocop_todo.yml +10 -16
- data/CHANGELOG.md +48 -1
- data/Dockerfile +23 -0
- data/Gemfile +15 -7
- data/README.markdown +44 -5
- data/Rakefile +1 -0
- data/activerecord-import.gemspec +4 -0
- data/benchmarks/benchmark.rb +3 -3
- data/benchmarks/lib/base.rb +2 -2
- data/benchmarks/lib/cli_parser.rb +2 -2
- data/docker-compose.yml +34 -0
- data/gemfiles/7.1.gemfile +3 -0
- data/gemfiles/7.2.gemfile +3 -0
- data/lib/activerecord-import/active_record/adapters/trilogy_adapter.rb +8 -0
- data/lib/activerecord-import/adapters/abstract_adapter.rb +6 -5
- data/lib/activerecord-import/adapters/mysql_adapter.rb +24 -18
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +26 -18
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +29 -23
- data/lib/activerecord-import/adapters/trilogy_adapter.rb +7 -0
- data/lib/activerecord-import/import.rb +63 -28
- data/lib/activerecord-import/value_sets_parser.rb +1 -0
- data/lib/activerecord-import/version.rb +1 -1
- data/lib/activerecord-import.rb +0 -1
- data/test/adapters/trilogy.rb +9 -0
- data/test/database.yml.sample +7 -0
- data/test/github/database.yml +4 -0
- data/test/jdbcmysql/import_test.rb +3 -3
- data/test/jdbcpostgresql/import_test.rb +2 -2
- data/test/jdbcsqlite3/import_test.rb +2 -2
- data/test/makara_postgis/import_test.rb +2 -2
- data/test/models/author.rb +7 -0
- data/test/models/bike_maker.rb +1 -0
- data/test/models/book.rb +5 -2
- data/test/models/composite_book.rb +19 -0
- data/test/models/composite_chapter.rb +9 -0
- data/test/models/customer.rb +14 -4
- data/test/models/order.rb +13 -4
- data/test/models/tag.rb +6 -1
- data/test/models/tag_alias.rb +7 -1
- data/test/models/topic.rb +5 -0
- data/test/models/widget.rb +10 -3
- data/test/mysql2/import_test.rb +3 -3
- data/test/mysql2_makara/import_test.rb +3 -3
- data/test/mysqlspatial2/import_test.rb +3 -3
- data/test/postgis/import_test.rb +2 -2
- data/test/postgresql/import_test.rb +2 -2
- data/test/schema/generic_schema.rb +4 -1
- data/test/schema/jdbcpostgresql_schema.rb +1 -1
- data/test/schema/postgis_schema.rb +1 -1
- data/test/schema/postgresql_schema.rb +35 -4
- data/test/sqlite3/import_test.rb +2 -2
- data/test/support/postgresql/import_examples.rb +12 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +67 -10
- data/test/support/shared_examples/recursive_import.rb +67 -1
- data/test/test_helper.rb +6 -4
- data/test/trilogy/import_test.rb +7 -0
- data/test/value_sets_bytes_parser_test.rb +1 -1
- data/test/value_sets_records_parser_test.rb +1 -1
- metadata +24 -7
data/test/models/bike_maker.rb
CHANGED
data/test/models/book.rb
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
class Book < ActiveRecord::Base
|
4
4
|
belongs_to :topic, inverse_of: :books
|
5
|
-
|
6
|
-
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
6
|
+
belongs_to :tag, foreign_key: [:tag_id, :parent_id] unless ENV["SKIP_COMPOSITE_PK"]
|
7
|
+
else
|
8
|
+
belongs_to :tag, query_constraints: [:tag_id, :parent_id] unless ENV["SKIP_COMPOSITE_PK"]
|
9
|
+
end
|
7
10
|
has_many :chapters, inverse_of: :book
|
8
11
|
has_many :discounts, as: :discountable
|
9
12
|
has_many :end_notes, inverse_of: :book
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CompositeBook < ActiveRecord::Base
|
4
|
+
self.primary_key = %i[id author_id]
|
5
|
+
belongs_to :author
|
6
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
7
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
8
|
+
has_many :composite_chapters, inverse_of: :composite_book,
|
9
|
+
foreign_key: [:id, :author_id]
|
10
|
+
end
|
11
|
+
else
|
12
|
+
has_many :composite_chapters, inverse_of: :composite_book,
|
13
|
+
query_constraints: [:id, :author_id]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.sequence_name
|
17
|
+
"composite_book_id_seq"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CompositeChapter < ActiveRecord::Base
|
4
|
+
if ENV['AR_VERSION'].to_f >= 7.1
|
5
|
+
belongs_to :composite_book, inverse_of: :composite_chapters,
|
6
|
+
query_constraints: [:composite_book_id, :author_id]
|
7
|
+
end
|
8
|
+
validates :title, presence: true
|
9
|
+
end
|
data/test/models/customer.rb
CHANGED
@@ -1,8 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Customer < ActiveRecord::Base
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
6
|
+
has_many :orders,
|
7
|
+
inverse_of: :customer,
|
8
|
+
primary_key: %i(account_id id),
|
9
|
+
foreign_key: %i(account_id customer_id)
|
10
|
+
else
|
11
|
+
has_many :orders,
|
12
|
+
inverse_of: :customer,
|
13
|
+
primary_key: %i(account_id id),
|
14
|
+
query_constraints: %i(account_id customer_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
8
18
|
end
|
data/test/models/order.rb
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Order < ActiveRecord::Base
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
6
|
+
belongs_to :customer,
|
7
|
+
inverse_of: :orders,
|
8
|
+
primary_key: %i(account_id id),
|
9
|
+
foreign_key: %i(account_id customer_id)
|
10
|
+
else
|
11
|
+
belongs_to :customer,
|
12
|
+
inverse_of: :orders,
|
13
|
+
primary_key: %i(account_id id),
|
14
|
+
query_constraints: %i(account_id customer_id)
|
15
|
+
end
|
16
|
+
end
|
8
17
|
end
|
data/test/models/tag.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Tag < ActiveRecord::Base
|
4
|
-
|
4
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
5
|
+
self.primary_keys = :tag_id, :publisher_id unless ENV["SKIP_COMPOSITE_PK"]
|
6
|
+
else
|
7
|
+
self.primary_key = [:tag_id, :publisher_id] unless ENV["SKIP_COMPOSITE_PK"]
|
8
|
+
end
|
9
|
+
self.primary_key = [:tag_id, :publisher_id] unless ENV["SKIP_COMPOSITE_PK"]
|
5
10
|
has_many :books, inverse_of: :tag
|
6
11
|
has_many :tag_aliases, inverse_of: :tag
|
7
12
|
end
|
data/test/models/tag_alias.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class TagAlias < ActiveRecord::Base
|
4
|
-
|
4
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
6
|
+
belongs_to :tag, foreign_key: [:tag_id, :parent_id], required: true
|
7
|
+
else
|
8
|
+
belongs_to :tag, query_constraints: [:tag_id, :parent_id], required: true
|
9
|
+
end
|
10
|
+
end
|
5
11
|
end
|
data/test/models/topic.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Topic < ActiveRecord::Base
|
4
|
+
if ENV['AR_VERSION'].to_f >= 6.0
|
5
|
+
self.ignored_columns = [:priority]
|
6
|
+
end
|
7
|
+
alias_attribute :name, :title
|
8
|
+
|
4
9
|
validates_presence_of :author_name
|
5
10
|
validates :title, numericality: { only_integer: true }, on: :context_test
|
6
11
|
validates :title, uniqueness: true
|
data/test/models/widget.rb
CHANGED
@@ -19,8 +19,15 @@ class Widget < ActiveRecord::Base
|
|
19
19
|
|
20
20
|
default_scope -> { where(active: true) }
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
if ENV['AR_VERSION'].to_f >= 7.1
|
23
|
+
serialize :data, coder: YAML
|
24
|
+
serialize :json_data, coder: JSON
|
25
|
+
serialize :custom_data, coder: CustomCoder.new
|
26
|
+
else
|
27
|
+
serialize :data, Hash
|
28
|
+
serialize :json_data, JSON
|
29
|
+
serialize :custom_data, CustomCoder.new
|
30
|
+
end
|
31
|
+
|
24
32
|
serialize :unspecified_data
|
25
|
-
serialize :custom_data, CustomCoder.new
|
26
33
|
end
|
data/test/mysql2/import_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path(File.dirname(__FILE__)
|
4
|
-
require File.expand_path(File.dirname(__FILE__)
|
5
|
-
require File.expand_path(File.dirname(__FILE__)
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/assertions")
|
5
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/mysql/import_examples")
|
6
6
|
|
7
7
|
should_support_mysql_import_functionality
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path(File.dirname(__FILE__)
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
4
|
|
5
|
-
require File.expand_path(File.dirname(__FILE__)
|
6
|
-
require File.expand_path(File.dirname(__FILE__)
|
5
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/assertions")
|
6
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/mysql/import_examples")
|
7
7
|
|
8
8
|
should_support_mysql_import_functionality
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path(File.dirname(__FILE__)
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
4
|
|
5
|
-
require File.expand_path(File.dirname(__FILE__)
|
6
|
-
require File.expand_path(File.dirname(__FILE__)
|
5
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/assertions")
|
6
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/mysql/import_examples")
|
7
7
|
|
8
8
|
should_support_mysql_import_functionality
|
data/test/postgis/import_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path(File.dirname(__FILE__)
|
4
|
-
require File.expand_path(File.dirname(__FILE__)
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/postgresql/import_examples")
|
5
5
|
|
6
6
|
should_support_postgresql_import_functionality
|
7
7
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path(File.dirname(__FILE__)
|
4
|
-
require File.expand_path(File.dirname(__FILE__)
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/postgresql/import_examples")
|
5
5
|
|
6
6
|
should_support_postgresql_import_functionality
|
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
ActiveRecord::Schema.define do
|
4
4
|
create_table :schema_info, force: :cascade do |t|
|
5
|
-
t.integer :version
|
5
|
+
t.integer :version
|
6
6
|
end
|
7
|
+
add_index :schema_info, :version, unique: true
|
8
|
+
|
7
9
|
SchemaInfo.create version: SchemaInfo::VERSION
|
8
10
|
|
9
11
|
create_table :group, force: :cascade do |t|
|
@@ -22,6 +24,7 @@ ActiveRecord::Schema.define do
|
|
22
24
|
t.boolean :approved, default: '1'
|
23
25
|
t.integer :replies_count
|
24
26
|
t.integer :parent_id
|
27
|
+
t.integer :priority, default: 0
|
25
28
|
t.string :type
|
26
29
|
t.datetime :created_at
|
27
30
|
t.datetime :created_on
|
@@ -8,10 +8,7 @@ ActiveRecord::Schema.define do
|
|
8
8
|
# create ENUM if it does not exist yet
|
9
9
|
begin
|
10
10
|
execute('CREATE TYPE vendor_type AS ENUM (\'wholesaler\', \'retailer\');')
|
11
|
-
rescue ActiveRecord::StatementInvalid
|
12
|
-
# since PostgreSQL does not support IF NOT EXISTS when creating a TYPE,
|
13
|
-
# rescue the error and check the error class
|
14
|
-
raise unless e.cause.is_a? PG::DuplicateObject
|
11
|
+
rescue ActiveRecord::StatementInvalid
|
15
12
|
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'wholesaler\';')
|
16
13
|
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'retailer\';')
|
17
14
|
end
|
@@ -60,4 +57,38 @@ ActiveRecord::Schema.define do
|
|
60
57
|
end
|
61
58
|
|
62
59
|
add_index :alarms, [:device_id, :alarm_type], unique: true, where: 'status <> 0'
|
60
|
+
|
61
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
62
|
+
create_table :authors, force: :cascade do |t|
|
63
|
+
t.string :name
|
64
|
+
end
|
65
|
+
|
66
|
+
execute %(
|
67
|
+
DROP SEQUENCE IF EXISTS composite_book_id_seq CASCADE;
|
68
|
+
CREATE SEQUENCE composite_book_id_seq
|
69
|
+
AS integer
|
70
|
+
START WITH 1
|
71
|
+
INCREMENT BY 1
|
72
|
+
NO MINVALUE
|
73
|
+
NO MAXVALUE
|
74
|
+
CACHE 1;
|
75
|
+
|
76
|
+
DROP TABLE IF EXISTS composite_books;
|
77
|
+
CREATE TABLE composite_books (
|
78
|
+
id bigint DEFAULT nextval('composite_book_id_seq'::regclass) NOT NULL,
|
79
|
+
title character varying,
|
80
|
+
author_id bigint
|
81
|
+
);
|
82
|
+
|
83
|
+
ALTER TABLE ONLY composite_books ADD CONSTRAINT fk_rails_040a418131 FOREIGN KEY (author_id) REFERENCES authors(id);
|
84
|
+
).split.join(' ').strip
|
85
|
+
end
|
86
|
+
|
87
|
+
create_table :composite_chapters, force: :cascade do |t|
|
88
|
+
t.string :title
|
89
|
+
t.integer :composite_book_id, null: false
|
90
|
+
t.integer :author_id, null: false
|
91
|
+
t.datetime :created_at
|
92
|
+
t.datetime :updated_at
|
93
|
+
end
|
63
94
|
end
|
data/test/sqlite3/import_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path(File.dirname(__FILE__)
|
4
|
-
require File.expand_path(File.dirname(__FILE__)
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/sqlite3/import_examples")
|
5
5
|
|
6
6
|
should_support_sqlite3_import_functionality
|
@@ -351,6 +351,18 @@ def should_support_postgresql_import_functionality
|
|
351
351
|
assert_equal db_customer.orders.last, db_order
|
352
352
|
assert_not_equal db_order.customer_id, nil
|
353
353
|
end
|
354
|
+
|
355
|
+
it "should import models with auto-incrementing ID successfully" do
|
356
|
+
author = Author.create!(name: "Foo Barson")
|
357
|
+
|
358
|
+
books = []
|
359
|
+
2.times do |i|
|
360
|
+
books << CompositeBook.new(author_id: author.id, title: "book #{i}")
|
361
|
+
end
|
362
|
+
assert_difference "CompositeBook.count", +2 do
|
363
|
+
CompositeBook.import books
|
364
|
+
end
|
365
|
+
end
|
354
366
|
end
|
355
367
|
end
|
356
368
|
end
|
@@ -26,7 +26,7 @@ def should_support_basic_on_duplicate_key_update
|
|
26
26
|
User.import(updated_users, on_duplicate_key_update: [:name])
|
27
27
|
assert User.count == updated_users.length
|
28
28
|
User.all.each_with_index do |user, i|
|
29
|
-
assert_equal user.name, users[i].name
|
29
|
+
assert_equal user.name, "#{users[i].name} Rothschild"
|
30
30
|
assert_equal 1, user.lock_version
|
31
31
|
end
|
32
32
|
end
|
@@ -50,7 +50,7 @@ def should_support_basic_on_duplicate_key_update
|
|
50
50
|
User.import(columns, updated_values, on_duplicate_key_update: [:name])
|
51
51
|
assert User.count == updated_values.length
|
52
52
|
User.all.each_with_index do |user, i|
|
53
|
-
assert_equal user.name, users[i].name
|
53
|
+
assert_equal user.name, "#{users[i].name} Rothschild"
|
54
54
|
assert_equal 1, user.lock_version
|
55
55
|
end
|
56
56
|
end
|
@@ -72,7 +72,7 @@ def should_support_basic_on_duplicate_key_update
|
|
72
72
|
User.import(updated_values, on_duplicate_key_update: [:name])
|
73
73
|
assert User.count == updated_values.length
|
74
74
|
User.all.each_with_index do |user, i|
|
75
|
-
assert_equal user.name, users[i].name
|
75
|
+
assert_equal user.name, "#{users[i].name} Rothschild"
|
76
76
|
assert_equal 1, user.lock_version
|
77
77
|
end
|
78
78
|
updated_values2 = User.all.map do |user|
|
@@ -82,7 +82,7 @@ def should_support_basic_on_duplicate_key_update
|
|
82
82
|
User.import(updated_values2, on_duplicate_key_update: [:name])
|
83
83
|
assert User.count == updated_values2.length
|
84
84
|
User.all.each_with_index do |user, i|
|
85
|
-
assert_equal user.name, users[i].name
|
85
|
+
assert_equal user.name, "#{users[i].name} Rothschild jr."
|
86
86
|
assert_equal 2, user.lock_version
|
87
87
|
end
|
88
88
|
end
|
@@ -104,7 +104,7 @@ def should_support_basic_on_duplicate_key_update
|
|
104
104
|
Account.import(updated_accounts, on_duplicate_key_update: [:id, :name])
|
105
105
|
assert Account.count == updated_accounts.length
|
106
106
|
Account.all.each_with_index do |user, i|
|
107
|
-
assert_equal user.name, accounts[i].name
|
107
|
+
assert_equal user.name, "#{accounts[i].name} Rothschild"
|
108
108
|
assert_equal 1, user.lock
|
109
109
|
end
|
110
110
|
end
|
@@ -128,7 +128,7 @@ def should_support_basic_on_duplicate_key_update
|
|
128
128
|
Account.import(columns, updated_values, on_duplicate_key_update: [:name])
|
129
129
|
assert Account.count == updated_values.length
|
130
130
|
Account.all.each_with_index do |user, i|
|
131
|
-
assert_equal user.name, accounts[i].name
|
131
|
+
assert_equal user.name, "#{accounts[i].name} Rothschild"
|
132
132
|
assert_equal 1, user.lock
|
133
133
|
end
|
134
134
|
end
|
@@ -150,7 +150,7 @@ def should_support_basic_on_duplicate_key_update
|
|
150
150
|
Account.import(updated_values, on_duplicate_key_update: [:name])
|
151
151
|
assert Account.count == updated_values.length
|
152
152
|
Account.all.each_with_index do |user, i|
|
153
|
-
assert_equal user.name, accounts[i].name
|
153
|
+
assert_equal user.name, "#{accounts[i].name} Rothschild"
|
154
154
|
assert_equal 1, user.lock
|
155
155
|
end
|
156
156
|
end
|
@@ -172,10 +172,11 @@ def should_support_basic_on_duplicate_key_update
|
|
172
172
|
Bike::Maker.import(updated_makers, on_duplicate_key_update: [:name])
|
173
173
|
assert Bike::Maker.count == updated_makers.length
|
174
174
|
Bike::Maker.all.each_with_index do |maker, i|
|
175
|
-
assert_equal maker.name, makers[i].name
|
175
|
+
assert_equal maker.name, "#{makers[i].name} bikes"
|
176
176
|
assert_equal 1, maker.lock_version
|
177
177
|
end
|
178
178
|
end
|
179
|
+
|
179
180
|
it 'update the lock_version of models separated by namespaces by array' do
|
180
181
|
makers = [
|
181
182
|
Bike::Maker.new(name: 'Yamaha'),
|
@@ -195,7 +196,7 @@ def should_support_basic_on_duplicate_key_update
|
|
195
196
|
Bike::Maker.import(columns, updated_values, on_duplicate_key_update: [:name])
|
196
197
|
assert Bike::Maker.count == updated_values.length
|
197
198
|
Bike::Maker.all.each_with_index do |maker, i|
|
198
|
-
assert_equal maker.name, makers[i].name
|
199
|
+
assert_equal maker.name, "#{makers[i].name} bikes"
|
199
200
|
assert_equal 1, maker.lock_version
|
200
201
|
end
|
201
202
|
end
|
@@ -217,11 +218,39 @@ def should_support_basic_on_duplicate_key_update
|
|
217
218
|
Bike::Maker.import(updated_values, on_duplicate_key_update: [:name])
|
218
219
|
assert Bike::Maker.count == updated_values.length
|
219
220
|
Bike::Maker.all.each_with_index do |maker, i|
|
220
|
-
assert_equal maker.name, makers[i].name
|
221
|
+
assert_equal maker.name, "#{makers[i].name} bikes"
|
221
222
|
assert_equal 1, maker.lock_version
|
222
223
|
end
|
223
224
|
end
|
224
225
|
end
|
226
|
+
|
227
|
+
context 'with locking disabled' do
|
228
|
+
it 'does not update the lock_version' do
|
229
|
+
users = [
|
230
|
+
User.new(name: 'Salomon'),
|
231
|
+
User.new(name: 'Nathan')
|
232
|
+
]
|
233
|
+
User.import(users)
|
234
|
+
assert User.count == users.length
|
235
|
+
User.all.each do |user|
|
236
|
+
assert_equal 0, user.lock_version
|
237
|
+
end
|
238
|
+
updated_users = User.all.map do |user|
|
239
|
+
user.name += ' Rothschild'
|
240
|
+
user
|
241
|
+
end
|
242
|
+
|
243
|
+
ActiveRecord::Base.lock_optimistically = false # Disable locking
|
244
|
+
User.import(updated_users, on_duplicate_key_update: [:name])
|
245
|
+
ActiveRecord::Base.lock_optimistically = true # Enable locking
|
246
|
+
|
247
|
+
assert User.count == updated_users.length
|
248
|
+
User.all.each_with_index do |user, i|
|
249
|
+
assert_equal user.name, "#{users[i].name} Rothschild"
|
250
|
+
assert_equal 0, user.lock_version
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
225
254
|
end
|
226
255
|
|
227
256
|
context "with :on_duplicate_key_update" do
|
@@ -316,6 +345,34 @@ def should_support_basic_on_duplicate_key_update
|
|
316
345
|
should_support_on_duplicate_key_update
|
317
346
|
should_update_fields_mentioned
|
318
347
|
end
|
348
|
+
|
349
|
+
context "using column aliases" do
|
350
|
+
let(:columns) { %w( id title author_name author_email_address parent_id ) }
|
351
|
+
let(:update_columns) { %w(title author_email_address parent_id) }
|
352
|
+
|
353
|
+
context "with column aliases in column list" do
|
354
|
+
let(:columns) { %w( id name author_name author_email_address parent_id ) }
|
355
|
+
should_support_on_duplicate_key_update
|
356
|
+
should_update_fields_mentioned
|
357
|
+
end
|
358
|
+
|
359
|
+
context "with column aliases in update columns list" do
|
360
|
+
let(:update_columns) { %w(name author_email_address parent_id) }
|
361
|
+
should_support_on_duplicate_key_update
|
362
|
+
should_update_fields_mentioned
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
if ENV['AR_VERSION'].to_i >= 6.0
|
367
|
+
context "using ignored columns" do
|
368
|
+
let(:columns) { %w( id title author_name author_email_address parent_id priority ) }
|
369
|
+
let(:values) { [[99, "Book", "John Doe", "john@doe.com", 17, 1]] }
|
370
|
+
let(:update_columns) { %w(name author_email_address parent_id priority) }
|
371
|
+
let(:updated_values) { [[99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57, 2]] }
|
372
|
+
should_support_on_duplicate_key_update
|
373
|
+
should_update_fields_mentioned
|
374
|
+
end
|
375
|
+
end
|
319
376
|
end
|
320
377
|
|
321
378
|
context "with a table that has a non-standard primary key" do
|
@@ -147,7 +147,7 @@ def should_support_recursive_import
|
|
147
147
|
end
|
148
148
|
|
149
149
|
books.each do |book|
|
150
|
-
|
150
|
+
assert_nil book.topic_id, nil
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
@@ -165,6 +165,24 @@ def should_support_recursive_import
|
|
165
165
|
assert_equal 1, tags[0].tag_id
|
166
166
|
assert_equal 2, tags[1].tag_id
|
167
167
|
end
|
168
|
+
|
169
|
+
if ENV['AR_VERSION'].to_f >= 7.1
|
170
|
+
it "should import models with auto-incrementing ID successfully with recursive set to true" do
|
171
|
+
author = Author.create!(name: "Foo Barson")
|
172
|
+
books = []
|
173
|
+
2.times do |i|
|
174
|
+
books << CompositeBook.new(author_id: author.id, title: "Book #{i}", composite_chapters: [
|
175
|
+
CompositeChapter.new(title: "Book #{i} composite chapter 1"),
|
176
|
+
CompositeChapter.new(title: "Book #{i} composite chapter 2"),
|
177
|
+
])
|
178
|
+
end
|
179
|
+
assert_difference "CompositeBook.count", +2 do
|
180
|
+
assert_difference "CompositeChapter.count", +4 do
|
181
|
+
CompositeBook.import books, recursive: true
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
168
186
|
end
|
169
187
|
end
|
170
188
|
|
@@ -213,6 +231,54 @@ def should_support_recursive_import
|
|
213
231
|
end
|
214
232
|
end
|
215
233
|
end
|
234
|
+
|
235
|
+
describe "recursive_on_duplicate_key_update" do
|
236
|
+
let(:new_topics) { Build(1, :topic_with_book) }
|
237
|
+
|
238
|
+
setup do
|
239
|
+
Topic.import new_topics, recursive: true
|
240
|
+
end
|
241
|
+
|
242
|
+
it "updates associated objects" do
|
243
|
+
new_author_name = 'Richard Bachman'
|
244
|
+
topic = new_topics.first
|
245
|
+
topic.books.each do |book|
|
246
|
+
book.author_name = new_author_name
|
247
|
+
end
|
248
|
+
|
249
|
+
assert_nothing_raised do
|
250
|
+
Topic.import new_topics,
|
251
|
+
recursive: true,
|
252
|
+
on_duplicate_key_update: [:id],
|
253
|
+
recursive_on_duplicate_key_update: {
|
254
|
+
books: { conflict_target: [:id], columns: [:author_name] }
|
255
|
+
}
|
256
|
+
end
|
257
|
+
Topic.find(topic.id).books.each do |book|
|
258
|
+
assert_equal new_author_name, book.author_name
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
it "updates nested associated objects" do
|
263
|
+
new_chapter_title = 'The Final Chapter'
|
264
|
+
book = new_topics.first.books.first
|
265
|
+
book.author_name = 'Richard Bachman'
|
266
|
+
|
267
|
+
example_chapter = book.chapters.first
|
268
|
+
example_chapter.title = new_chapter_title
|
269
|
+
|
270
|
+
assert_nothing_raised do
|
271
|
+
Topic.import new_topics,
|
272
|
+
recursive: true,
|
273
|
+
on_duplicate_key_update: [:id],
|
274
|
+
recursive_on_duplicate_key_update: {
|
275
|
+
books: { conflict_target: [:id], columns: [:author_name] },
|
276
|
+
chapters: { conflict_target: [:id], columns: [:title] }
|
277
|
+
}
|
278
|
+
end
|
279
|
+
assert_equal new_chapter_title, Chapter.find(example_chapter.id).title
|
280
|
+
end
|
281
|
+
end
|
216
282
|
end
|
217
283
|
|
218
284
|
# If returning option is provided, it is only applied to top level models so that SQL with invalid
|
data/test/test_helper.rb
CHANGED
@@ -24,7 +24,7 @@ if ActiveSupport::VERSION::STRING < "4.0"
|
|
24
24
|
require 'mocha/test_unit'
|
25
25
|
else
|
26
26
|
require 'active_support/testing/autorun'
|
27
|
-
require "mocha/
|
27
|
+
require "mocha/minitest"
|
28
28
|
end
|
29
29
|
|
30
30
|
require 'timecop'
|
@@ -33,7 +33,9 @@ require 'chronic'
|
|
33
33
|
begin
|
34
34
|
require 'composite_primary_keys'
|
35
35
|
rescue LoadError
|
36
|
-
ENV[
|
36
|
+
if ENV['AR_VERSION'].to_f <= 7.1
|
37
|
+
ENV['SKIP_COMPOSITE_PK'] = 'true'
|
38
|
+
end
|
37
39
|
end
|
38
40
|
|
39
41
|
# Support MySQL 5.7
|
@@ -84,7 +86,7 @@ ActiveSupport::Notifications.subscribe(/active_record.sql/) do |_, _, _, _, hsh|
|
|
84
86
|
end
|
85
87
|
|
86
88
|
require "factory_bot"
|
87
|
-
Dir[File.dirname(__FILE__)
|
89
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |file| require file }
|
88
90
|
|
89
91
|
# Load base/generic schema
|
90
92
|
require test_dir.join("schema/version")
|
@@ -92,7 +94,7 @@ require test_dir.join("schema/generic_schema")
|
|
92
94
|
adapter_schema = test_dir.join("schema/#{adapter}_schema.rb")
|
93
95
|
require adapter_schema if File.exist?(adapter_schema)
|
94
96
|
|
95
|
-
Dir[File.dirname(__FILE__)
|
97
|
+
Dir["#{File.dirname(__FILE__)}/models/*.rb"].sort.each { |file| require file }
|
96
98
|
|
97
99
|
# Prevent this deprecation warning from breaking the tests.
|
98
100
|
Rake::FileList.send(:remove_method, :import)
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/assertions")
|
5
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/mysql/import_examples")
|
6
|
+
|
7
|
+
should_support_mysql_import_functionality
|