activerecord-import 1.0.2 → 1.7.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 +151 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +74 -8
- data/.rubocop_todo.yml +10 -16
- data/Brewfile +3 -1
- data/CHANGELOG.md +138 -3
- data/Dockerfile +23 -0
- data/Gemfile +24 -14
- data/LICENSE +21 -56
- data/README.markdown +108 -60
- data/Rakefile +3 -0
- data/activerecord-import.gemspec +6 -5
- data/benchmarks/benchmark.rb +10 -4
- data/benchmarks/lib/base.rb +4 -2
- data/benchmarks/lib/cli_parser.rb +4 -2
- data/benchmarks/lib/float.rb +2 -0
- data/benchmarks/lib/mysql2_benchmark.rb +2 -0
- data/benchmarks/lib/output_to_csv.rb +2 -0
- data/benchmarks/lib/output_to_html.rb +4 -2
- data/benchmarks/models/test_innodb.rb +2 -0
- data/benchmarks/models/test_memory.rb +2 -0
- data/benchmarks/models/test_myisam.rb +2 -0
- data/benchmarks/schema/{mysql_schema.rb → mysql2_schema.rb} +2 -0
- data/docker-compose.yml +34 -0
- data/gemfiles/4.2.gemfile +2 -0
- data/gemfiles/5.0.gemfile +2 -0
- data/gemfiles/5.1.gemfile +2 -0
- data/gemfiles/5.2.gemfile +2 -0
- data/gemfiles/6.0.gemfile +4 -1
- data/gemfiles/6.1.gemfile +4 -1
- data/gemfiles/7.0.gemfile +4 -0
- data/gemfiles/7.1.gemfile +3 -0
- data/lib/activerecord-import/active_record/adapters/abstract_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb +6 -4
- data/lib/activerecord-import/active_record/adapters/jdbcpostgresql_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/jdbcsqlite3_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/postgresql_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/seamless_database_pool_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/trilogy_adapter.rb +8 -0
- data/lib/activerecord-import/adapters/abstract_adapter.rb +14 -5
- data/lib/activerecord-import/adapters/em_mysql2_adapter.rb +2 -0
- data/lib/activerecord-import/adapters/mysql2_adapter.rb +2 -0
- data/lib/activerecord-import/adapters/mysql_adapter.rb +33 -25
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +69 -56
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +39 -39
- data/lib/activerecord-import/adapters/trilogy_adapter.rb +7 -0
- data/lib/activerecord-import/base.rb +10 -2
- data/lib/activerecord-import/import.rb +162 -65
- data/lib/activerecord-import/mysql2.rb +2 -0
- data/lib/activerecord-import/postgresql.rb +2 -0
- data/lib/activerecord-import/sqlite3.rb +2 -0
- data/lib/activerecord-import/synchronize.rb +3 -1
- data/lib/activerecord-import/value_sets_parser.rb +5 -0
- data/lib/activerecord-import/version.rb +3 -1
- data/lib/activerecord-import.rb +2 -1
- data/test/adapters/jdbcmysql.rb +2 -0
- data/test/adapters/jdbcpostgresql.rb +2 -0
- data/test/adapters/jdbcsqlite3.rb +2 -0
- data/test/adapters/makara_postgis.rb +2 -0
- data/test/adapters/mysql2.rb +2 -0
- data/test/adapters/mysql2_makara.rb +2 -0
- data/test/adapters/mysql2spatial.rb +2 -0
- data/test/adapters/postgis.rb +2 -0
- data/test/adapters/postgresql.rb +2 -0
- data/test/adapters/postgresql_makara.rb +2 -0
- data/test/adapters/seamless_database_pool.rb +2 -0
- data/test/adapters/spatialite.rb +2 -0
- data/test/adapters/sqlite3.rb +2 -0
- data/test/adapters/trilogy.rb +9 -0
- data/test/database.yml.sample +7 -0
- data/test/{travis → github}/database.yml +7 -1
- data/test/import_test.rb +93 -2
- data/test/jdbcmysql/import_test.rb +5 -3
- data/test/jdbcpostgresql/import_test.rb +4 -2
- data/test/jdbcsqlite3/import_test.rb +4 -2
- data/test/makara_postgis/import_test.rb +4 -2
- data/test/models/account.rb +2 -0
- data/test/models/alarm.rb +2 -0
- data/test/models/animal.rb +8 -0
- data/test/models/author.rb +7 -0
- data/test/models/bike_maker.rb +3 -0
- data/test/models/book.rb +7 -2
- data/test/models/car.rb +2 -0
- data/test/models/card.rb +5 -0
- data/test/models/chapter.rb +2 -0
- data/test/models/composite_book.rb +19 -0
- data/test/models/composite_chapter.rb +9 -0
- data/test/models/customer.rb +18 -0
- data/test/models/deck.rb +8 -0
- data/test/models/dictionary.rb +2 -0
- data/test/models/discount.rb +2 -0
- data/test/models/end_note.rb +2 -0
- data/test/models/group.rb +2 -0
- data/test/models/order.rb +17 -0
- data/test/models/playing_card.rb +4 -0
- data/test/models/promotion.rb +2 -0
- data/test/models/question.rb +2 -0
- data/test/models/rule.rb +2 -0
- data/test/models/tag.rb +9 -1
- data/test/models/tag_alias.rb +11 -0
- data/test/models/topic.rb +7 -0
- data/test/models/user.rb +2 -0
- data/test/models/user_token.rb +2 -0
- data/test/models/vendor.rb +2 -0
- data/test/models/widget.rb +2 -0
- data/test/mysql2/import_test.rb +5 -3
- data/test/mysql2_makara/import_test.rb +5 -3
- data/test/mysqlspatial2/import_test.rb +5 -3
- data/test/postgis/import_test.rb +4 -2
- data/test/postgresql/import_test.rb +4 -2
- data/test/schema/generic_schema.rb +37 -1
- data/test/schema/jdbcpostgresql_schema.rb +3 -1
- data/test/schema/mysql2_schema.rb +2 -0
- data/test/schema/postgis_schema.rb +3 -1
- data/test/schema/postgresql_schema.rb +47 -0
- data/test/schema/sqlite3_schema.rb +2 -0
- data/test/schema/version.rb +2 -0
- data/test/sqlite3/import_test.rb +4 -2
- data/test/support/active_support/test_case_extensions.rb +2 -0
- data/test/support/assertions.rb +2 -0
- data/test/support/factories.rb +2 -0
- data/test/support/generate.rb +4 -2
- data/test/support/mysql/import_examples.rb +2 -1
- data/test/support/postgresql/import_examples.rb +108 -2
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +2 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +78 -9
- data/test/support/shared_examples/recursive_import.rb +98 -1
- data/test/support/sqlite3/import_examples.rb +2 -1
- data/test/synchronize_test.rb +2 -0
- data/test/test_helper.rb +33 -6
- data/test/trilogy/import_test.rb +7 -0
- data/test/value_sets_bytes_parser_test.rb +3 -1
- data/test/value_sets_records_parser_test.rb +3 -1
- metadata +42 -16
- data/.travis.yml +0 -70
- data/gemfiles/3.2.gemfile +0 -2
- data/gemfiles/4.0.gemfile +0 -2
- data/gemfiles/4.1.gemfile +0 -2
data/test/models/vendor.rb
CHANGED
data/test/models/widget.rb
CHANGED
data/test/mysql2/import_test.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require File.expand_path(File.dirname(__FILE__)
|
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")
|
4
6
|
|
5
7
|
should_support_mysql_import_functionality
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path(File.dirname(__FILE__)
|
4
|
-
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
|
+
|
5
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/assertions")
|
6
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/mysql/import_examples")
|
5
7
|
|
6
8
|
should_support_mysql_import_functionality
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require File.expand_path(File.dirname(__FILE__)
|
4
|
-
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
|
+
|
5
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/assertions")
|
6
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/mysql/import_examples")
|
5
7
|
|
6
8
|
should_support_mysql_import_functionality
|
data/test/postgis/import_test.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
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/postgresql/import_examples")
|
3
5
|
|
4
6
|
should_support_postgresql_import_functionality
|
5
7
|
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
2
|
-
|
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/postgresql/import_examples")
|
3
5
|
|
4
6
|
should_support_postgresql_import_functionality
|
@@ -1,7 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
ActiveRecord::Schema.define do
|
2
4
|
create_table :schema_info, force: :cascade do |t|
|
3
|
-
t.integer :version
|
5
|
+
t.integer :version
|
4
6
|
end
|
7
|
+
add_index :schema_info, :version, unique: true
|
8
|
+
|
5
9
|
SchemaInfo.create version: SchemaInfo::VERSION
|
6
10
|
|
7
11
|
create_table :group, force: :cascade do |t|
|
@@ -20,6 +24,7 @@ ActiveRecord::Schema.define do
|
|
20
24
|
t.boolean :approved, default: '1'
|
21
25
|
t.integer :replies_count
|
22
26
|
t.integer :parent_id
|
27
|
+
t.integer :priority, default: 0
|
23
28
|
t.string :type
|
24
29
|
t.datetime :created_at
|
25
30
|
t.datetime :created_on
|
@@ -52,6 +57,20 @@ ActiveRecord::Schema.define do
|
|
52
57
|
t.string :name
|
53
58
|
end
|
54
59
|
|
60
|
+
create_table :cards, force: :cascade do |t|
|
61
|
+
t.string :name
|
62
|
+
t.string :deck_type
|
63
|
+
t.integer :deck_id
|
64
|
+
end
|
65
|
+
|
66
|
+
create_table :decks, force: :cascade do |t|
|
67
|
+
t.string :name
|
68
|
+
end
|
69
|
+
|
70
|
+
create_table :playing_cards, force: :cascade do |t|
|
71
|
+
t.string :name
|
72
|
+
end
|
73
|
+
|
55
74
|
create_table :books, force: :cascade do |t|
|
56
75
|
t.string :title, null: false
|
57
76
|
t.string :publisher, null: false, default: 'Default Publisher'
|
@@ -190,5 +209,22 @@ ActiveRecord::Schema.define do
|
|
190
209
|
PRIMARY KEY (tag_id, publisher_id)
|
191
210
|
);
|
192
211
|
).split.join(' ').strip
|
212
|
+
|
213
|
+
create_table :tag_aliases, force: :cascade do |t|
|
214
|
+
t.integer :tag_id, null: false
|
215
|
+
t.integer :parent_id, null: false
|
216
|
+
t.string :alias, null: false
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
create_table :customers, force: :cascade do |t|
|
221
|
+
t.integer :account_id
|
222
|
+
t.string :name
|
223
|
+
end
|
224
|
+
|
225
|
+
create_table :orders, force: :cascade do |t|
|
226
|
+
t.integer :account_id
|
227
|
+
t.integer :customer_id
|
228
|
+
t.integer :amount
|
193
229
|
end
|
194
230
|
end
|
@@ -1,10 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
ActiveRecord::Schema.define do
|
2
4
|
execute('CREATE extension IF NOT EXISTS "hstore";')
|
3
5
|
execute('CREATE extension IF NOT EXISTS "pgcrypto";')
|
4
6
|
execute('CREATE extension IF NOT EXISTS "uuid-ossp";')
|
5
7
|
|
8
|
+
# create ENUM if it does not exist yet
|
9
|
+
begin
|
10
|
+
execute('CREATE TYPE vendor_type AS ENUM (\'wholesaler\', \'retailer\');')
|
11
|
+
rescue ActiveRecord::StatementInvalid
|
12
|
+
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'wholesaler\';')
|
13
|
+
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'retailer\';')
|
14
|
+
end
|
15
|
+
|
6
16
|
create_table :vendors, id: :uuid, force: :cascade do |t|
|
7
17
|
t.string :name, null: true
|
18
|
+
t.text :hours
|
8
19
|
t.text :preferences
|
9
20
|
|
10
21
|
if t.respond_to?(:json)
|
@@ -29,6 +40,8 @@ ActiveRecord::Schema.define do
|
|
29
40
|
t.text :json_data
|
30
41
|
end
|
31
42
|
|
43
|
+
t.column :vendor_type, :vendor_type
|
44
|
+
|
32
45
|
t.datetime :created_at
|
33
46
|
t.datetime :updated_at
|
34
47
|
end
|
@@ -44,4 +57,38 @@ ActiveRecord::Schema.define do
|
|
44
57
|
end
|
45
58
|
|
46
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
|
47
94
|
end
|
data/test/schema/version.rb
CHANGED
data/test/sqlite3/import_test.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
2
|
-
|
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/sqlite3/import_examples")
|
3
5
|
|
4
6
|
should_support_sqlite3_import_functionality
|
data/test/support/assertions.rb
CHANGED
data/test/support/factories.rb
CHANGED
data/test/support/generate.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ActiveSupport::TestCase
|
2
|
-
def Build(*args) # rubocop:disable
|
4
|
+
def Build(*args) # rubocop:disable Naming/MethodName
|
3
5
|
n = args.shift if args.first.is_a?(Numeric)
|
4
6
|
factory = args.shift
|
5
7
|
factory_bot_args = args.shift || {}
|
@@ -13,7 +15,7 @@ class ActiveSupport::TestCase
|
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
def Generate(*args) # rubocop:disable
|
18
|
+
def Generate(*args) # rubocop:disable Naming/MethodName
|
17
19
|
n = args.shift if args.first.is_a?(Numeric)
|
18
20
|
factory = args.shift
|
19
21
|
factory_bot_args = args.shift || {}
|
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
def should_support_postgresql_import_functionality
|
3
4
|
should_support_recursive_import
|
4
5
|
|
@@ -102,6 +103,8 @@ def should_support_postgresql_import_functionality
|
|
102
103
|
books.first.id.to_s
|
103
104
|
end
|
104
105
|
end
|
106
|
+
let(:true_returning_value) { ENV['AR_VERSION'].to_f >= 5.0 ? true : 't' }
|
107
|
+
let(:false_returning_value) { ENV['AR_VERSION'].to_f >= 5.0 ? false : 'f' }
|
105
108
|
|
106
109
|
it "creates records" do
|
107
110
|
assert_difference("Book.count", +1) { result }
|
@@ -116,6 +119,26 @@ def should_support_postgresql_import_functionality
|
|
116
119
|
assert_equal [%w(King It)], result.results
|
117
120
|
end
|
118
121
|
|
122
|
+
context "when given an empty array" do
|
123
|
+
let(:result) { Book.import([], returning: %w(title)) }
|
124
|
+
|
125
|
+
setup { result }
|
126
|
+
|
127
|
+
it "returns empty arrays for ids and results" do
|
128
|
+
assert_equal [], result.ids
|
129
|
+
assert_equal [], result.results
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when a returning column is a serialized attribute" do
|
134
|
+
let(:vendor) { Vendor.new(hours: { monday: '8-5' }) }
|
135
|
+
let(:result) { Vendor.import([vendor], returning: %w(hours)) }
|
136
|
+
|
137
|
+
it "creates records" do
|
138
|
+
assert_difference("Vendor.count", +1) { result }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
119
142
|
context "when primary key and returning overlap" do
|
120
143
|
let(:result) { Book.import(books, returning: %w(id title)) }
|
121
144
|
|
@@ -130,6 +153,34 @@ def should_support_postgresql_import_functionality
|
|
130
153
|
end
|
131
154
|
end
|
132
155
|
|
156
|
+
context "when returning is raw sql" do
|
157
|
+
let(:result) { Book.import(books, returning: "title, (xmax = '0') AS inserted") }
|
158
|
+
|
159
|
+
setup { result }
|
160
|
+
|
161
|
+
it "returns ids" do
|
162
|
+
assert_equal [book_id], result.ids
|
163
|
+
end
|
164
|
+
|
165
|
+
it "returns specified columns" do
|
166
|
+
assert_equal [['It', true_returning_value]], result.results
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "when returning contains raw sql" do
|
171
|
+
let(:result) { Book.import(books, returning: [:title, "id, (xmax = '0') AS inserted"]) }
|
172
|
+
|
173
|
+
setup { result }
|
174
|
+
|
175
|
+
it "returns ids" do
|
176
|
+
assert_equal [book_id], result.ids
|
177
|
+
end
|
178
|
+
|
179
|
+
it "returns specified columns" do
|
180
|
+
assert_equal [['It', book_id, true_returning_value]], result.results
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
133
184
|
context "setting model attributes" do
|
134
185
|
let(:code) { 'abc' }
|
135
186
|
let(:discount) { 0.10 }
|
@@ -159,6 +210,14 @@ def should_support_postgresql_import_functionality
|
|
159
210
|
assert_equal updated_promotion.discount, discount
|
160
211
|
end
|
161
212
|
end
|
213
|
+
|
214
|
+
context 'returning raw sql' do
|
215
|
+
let(:returning_columns) { [:discount, "(xmax = '0') AS inserted"] }
|
216
|
+
|
217
|
+
it "sets custom model attributes" do
|
218
|
+
assert_equal updated_promotion.inserted, false_returning_value
|
219
|
+
end
|
220
|
+
end
|
162
221
|
end
|
163
222
|
end
|
164
223
|
end
|
@@ -249,8 +308,19 @@ def should_support_postgresql_import_functionality
|
|
249
308
|
end
|
250
309
|
end
|
251
310
|
|
311
|
+
describe "with enum field" do
|
312
|
+
let(:vendor_type) { "retailer" }
|
313
|
+
it "imports the correct values for enum fields" do
|
314
|
+
vendor = Vendor.new(name: 'Vendor 1', vendor_type: vendor_type)
|
315
|
+
assert_difference "Vendor.count", +1 do
|
316
|
+
Vendor.import [vendor]
|
317
|
+
end
|
318
|
+
assert_equal(vendor_type, Vendor.first.vendor_type)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
252
322
|
describe "with binary field" do
|
253
|
-
let(:binary_value) { "\xE0'c\xB2\xB0\xB3Bh\\\xC2M\xB1m\\I\xC4r".force_encoding('ASCII-8BIT') }
|
323
|
+
let(:binary_value) { "\xE0'c\xB2\xB0\xB3Bh\\\xC2M\xB1m\\I\xC4r".dup.force_encoding('ASCII-8BIT') }
|
254
324
|
it "imports the correct values for binary fields" do
|
255
325
|
alarms = [Alarm.new(device_id: 1, alarm_type: 1, status: 1, secret_key: binary_value)]
|
256
326
|
assert_difference "Alarm.count", +1 do
|
@@ -259,6 +329,42 @@ def should_support_postgresql_import_functionality
|
|
259
329
|
assert_equal(binary_value, Alarm.first.secret_key)
|
260
330
|
end
|
261
331
|
end
|
332
|
+
|
333
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
334
|
+
describe "with composite foreign keys" do
|
335
|
+
let(:account_id) { 555 }
|
336
|
+
let(:customer) { Customer.new(account_id: account_id, name: "foo") }
|
337
|
+
let(:order) { Order.new(account_id: account_id, amount: 100, customer: customer) }
|
338
|
+
|
339
|
+
it "imports and correctly maps foreign keys" do
|
340
|
+
assert_difference "Customer.count", +1 do
|
341
|
+
Customer.import [customer]
|
342
|
+
end
|
343
|
+
|
344
|
+
assert_difference "Order.count", +1 do
|
345
|
+
Order.import [order]
|
346
|
+
end
|
347
|
+
|
348
|
+
db_customer = Customer.last
|
349
|
+
db_order = Order.last
|
350
|
+
|
351
|
+
assert_equal db_customer.orders.last, db_order
|
352
|
+
assert_not_equal db_order.customer_id, nil
|
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
|
366
|
+
end
|
367
|
+
end
|
262
368
|
end
|
263
369
|
|
264
370
|
def should_support_postgresql_upsert_functionality
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
def should_support_basic_on_duplicate_key_update
|
2
4
|
describe "#import" do
|
3
5
|
extend ActiveSupport::TestCase::ImportAssertions
|
@@ -24,7 +26,7 @@ def should_support_basic_on_duplicate_key_update
|
|
24
26
|
User.import(updated_users, on_duplicate_key_update: [:name])
|
25
27
|
assert User.count == updated_users.length
|
26
28
|
User.all.each_with_index do |user, i|
|
27
|
-
assert_equal user.name, users[i].name
|
29
|
+
assert_equal user.name, "#{users[i].name} Rothschild"
|
28
30
|
assert_equal 1, user.lock_version
|
29
31
|
end
|
30
32
|
end
|
@@ -48,7 +50,7 @@ def should_support_basic_on_duplicate_key_update
|
|
48
50
|
User.import(columns, updated_values, on_duplicate_key_update: [:name])
|
49
51
|
assert User.count == updated_values.length
|
50
52
|
User.all.each_with_index do |user, i|
|
51
|
-
assert_equal user.name, users[i].name
|
53
|
+
assert_equal user.name, "#{users[i].name} Rothschild"
|
52
54
|
assert_equal 1, user.lock_version
|
53
55
|
end
|
54
56
|
end
|
@@ -70,9 +72,19 @@ def should_support_basic_on_duplicate_key_update
|
|
70
72
|
User.import(updated_values, on_duplicate_key_update: [:name])
|
71
73
|
assert User.count == updated_values.length
|
72
74
|
User.all.each_with_index do |user, i|
|
73
|
-
assert_equal user.name, users[i].name
|
75
|
+
assert_equal user.name, "#{users[i].name} Rothschild"
|
74
76
|
assert_equal 1, user.lock_version
|
75
77
|
end
|
78
|
+
updated_values2 = User.all.map do |user|
|
79
|
+
user.name += ' jr.'
|
80
|
+
{ id: user.id, name: user.name }
|
81
|
+
end
|
82
|
+
User.import(updated_values2, on_duplicate_key_update: [:name])
|
83
|
+
assert User.count == updated_values2.length
|
84
|
+
User.all.each_with_index do |user, i|
|
85
|
+
assert_equal user.name, "#{users[i].name} Rothschild jr."
|
86
|
+
assert_equal 2, user.lock_version
|
87
|
+
end
|
76
88
|
end
|
77
89
|
|
78
90
|
it 'upsert optimistic lock columns other than lock_version by model' do
|
@@ -92,7 +104,7 @@ def should_support_basic_on_duplicate_key_update
|
|
92
104
|
Account.import(updated_accounts, on_duplicate_key_update: [:id, :name])
|
93
105
|
assert Account.count == updated_accounts.length
|
94
106
|
Account.all.each_with_index do |user, i|
|
95
|
-
assert_equal user.name, accounts[i].name
|
107
|
+
assert_equal user.name, "#{accounts[i].name} Rothschild"
|
96
108
|
assert_equal 1, user.lock
|
97
109
|
end
|
98
110
|
end
|
@@ -116,7 +128,7 @@ def should_support_basic_on_duplicate_key_update
|
|
116
128
|
Account.import(columns, updated_values, on_duplicate_key_update: [:name])
|
117
129
|
assert Account.count == updated_values.length
|
118
130
|
Account.all.each_with_index do |user, i|
|
119
|
-
assert_equal user.name, accounts[i].name
|
131
|
+
assert_equal user.name, "#{accounts[i].name} Rothschild"
|
120
132
|
assert_equal 1, user.lock
|
121
133
|
end
|
122
134
|
end
|
@@ -138,7 +150,7 @@ def should_support_basic_on_duplicate_key_update
|
|
138
150
|
Account.import(updated_values, on_duplicate_key_update: [:name])
|
139
151
|
assert Account.count == updated_values.length
|
140
152
|
Account.all.each_with_index do |user, i|
|
141
|
-
assert_equal user.name, accounts[i].name
|
153
|
+
assert_equal user.name, "#{accounts[i].name} Rothschild"
|
142
154
|
assert_equal 1, user.lock
|
143
155
|
end
|
144
156
|
end
|
@@ -160,10 +172,11 @@ def should_support_basic_on_duplicate_key_update
|
|
160
172
|
Bike::Maker.import(updated_makers, on_duplicate_key_update: [:name])
|
161
173
|
assert Bike::Maker.count == updated_makers.length
|
162
174
|
Bike::Maker.all.each_with_index do |maker, i|
|
163
|
-
assert_equal maker.name, makers[i].name
|
175
|
+
assert_equal maker.name, "#{makers[i].name} bikes"
|
164
176
|
assert_equal 1, maker.lock_version
|
165
177
|
end
|
166
178
|
end
|
179
|
+
|
167
180
|
it 'update the lock_version of models separated by namespaces by array' do
|
168
181
|
makers = [
|
169
182
|
Bike::Maker.new(name: 'Yamaha'),
|
@@ -183,7 +196,7 @@ def should_support_basic_on_duplicate_key_update
|
|
183
196
|
Bike::Maker.import(columns, updated_values, on_duplicate_key_update: [:name])
|
184
197
|
assert Bike::Maker.count == updated_values.length
|
185
198
|
Bike::Maker.all.each_with_index do |maker, i|
|
186
|
-
assert_equal maker.name, makers[i].name
|
199
|
+
assert_equal maker.name, "#{makers[i].name} bikes"
|
187
200
|
assert_equal 1, maker.lock_version
|
188
201
|
end
|
189
202
|
end
|
@@ -205,11 +218,39 @@ def should_support_basic_on_duplicate_key_update
|
|
205
218
|
Bike::Maker.import(updated_values, on_duplicate_key_update: [:name])
|
206
219
|
assert Bike::Maker.count == updated_values.length
|
207
220
|
Bike::Maker.all.each_with_index do |maker, i|
|
208
|
-
assert_equal maker.name, makers[i].name
|
221
|
+
assert_equal maker.name, "#{makers[i].name} bikes"
|
209
222
|
assert_equal 1, maker.lock_version
|
210
223
|
end
|
211
224
|
end
|
212
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
|
213
254
|
end
|
214
255
|
|
215
256
|
context "with :on_duplicate_key_update" do
|
@@ -304,6 +345,34 @@ def should_support_basic_on_duplicate_key_update
|
|
304
345
|
should_support_on_duplicate_key_update
|
305
346
|
should_update_fields_mentioned
|
306
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
|
307
376
|
end
|
308
377
|
|
309
378
|
context "with a table that has a non-standard primary key" do
|