activerecord-import 1.0.4 → 2.0.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 +159 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +76 -7
- data/.rubocop_todo.yml +10 -16
- data/Brewfile +3 -1
- data/CHANGELOG.md +143 -3
- data/Dockerfile +23 -0
- data/Gemfile +28 -24
- data/LICENSE +21 -56
- data/README.markdown +83 -27
- data/Rakefile +3 -0
- data/activerecord-import.gemspec +10 -5
- data/benchmarks/benchmark.rb +10 -6
- data/benchmarks/lib/base.rb +10 -5
- data/benchmarks/lib/cli_parser.rb +10 -6
- 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/5.2.gemfile +2 -0
- data/gemfiles/6.0.gemfile +3 -0
- data/gemfiles/6.1.gemfile +4 -1
- data/gemfiles/7.0.gemfile +4 -0
- data/gemfiles/7.1.gemfile +3 -0
- data/gemfiles/7.2.gemfile +3 -0
- data/gemfiles/8.0.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 +9 -6
- 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 +30 -21
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +68 -48
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +37 -30
- data/lib/activerecord-import/adapters/trilogy_adapter.rb +7 -0
- data/lib/activerecord-import/base.rb +3 -1
- data/lib/activerecord-import/import.rb +160 -58
- 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 +9 -3
- data/test/import_test.rb +108 -41
- 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 +9 -0
- data/test/models/bike_maker.rb +3 -0
- data/test/models/book.rb +12 -3
- 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 +12 -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 +8 -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 +12 -3
- 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 +38 -4
- 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 +3 -5
- 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 +7 -8
- data/test/support/postgresql/import_examples.rb +121 -53
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +2 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +69 -10
- data/test/support/shared_examples/recursive_import.rb +137 -1
- data/test/support/sqlite3/import_examples.rb +2 -1
- data/test/synchronize_test.rb +2 -0
- data/test/test_helper.rb +38 -24
- 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 +46 -22
- data/.travis.yml +0 -74
- data/gemfiles/3.2.gemfile +0 -2
- data/gemfiles/4.0.gemfile +0 -2
- data/gemfiles/4.1.gemfile +0 -2
- data/gemfiles/4.2.gemfile +0 -2
- data/gemfiles/5.0.gemfile +0 -2
- data/gemfiles/5.1.gemfile +0 -2
- data/lib/activerecord-import/mysql2.rb +0 -7
- data/lib/activerecord-import/postgresql.rb +0 -7
- data/lib/activerecord-import/sqlite3.rb +0 -7
@@ -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,3 +1,5 @@
|
|
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";')
|
@@ -6,16 +8,14 @@ ActiveRecord::Schema.define do
|
|
6
8
|
# create ENUM if it does not exist yet
|
7
9
|
begin
|
8
10
|
execute('CREATE TYPE vendor_type AS ENUM (\'wholesaler\', \'retailer\');')
|
9
|
-
rescue ActiveRecord::StatementInvalid
|
10
|
-
# since PostgreSQL does not support IF NOT EXISTS when creating a TYPE,
|
11
|
-
# rescue the error and check the error class
|
12
|
-
raise unless e.cause.is_a? PG::DuplicateObject
|
11
|
+
rescue ActiveRecord::StatementInvalid
|
13
12
|
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'wholesaler\';')
|
14
13
|
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'retailer\';')
|
15
14
|
end
|
16
15
|
|
17
16
|
create_table :vendors, id: :uuid, force: :cascade do |t|
|
18
17
|
t.string :name, null: true
|
18
|
+
t.text :hours
|
19
19
|
t.text :preferences
|
20
20
|
|
21
21
|
if t.respond_to?(:json)
|
@@ -57,4 +57,38 @@ ActiveRecord::Schema.define do
|
|
57
57
|
end
|
58
58
|
|
59
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
|
60
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
|
@@ -1,11 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ActiveSupport::TestCase
|
2
4
|
include ActiveRecord::TestFixtures
|
3
5
|
|
4
|
-
|
5
|
-
self.use_transactional_tests = true
|
6
|
-
else
|
7
|
-
self.use_transactional_fixtures = true
|
8
|
-
end
|
6
|
+
self.use_transactional_tests = true
|
9
7
|
|
10
8
|
class << self
|
11
9
|
def requires_active_record_version(version_string, &blk)
|
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_mysql_import_functionality
|
3
4
|
# Forcefully disable strict mode for this session.
|
4
5
|
ActiveRecord::Base.connection.execute "set sql_mode='STRICT_ALL_TABLES'"
|
@@ -83,14 +84,12 @@ def should_support_mysql_import_functionality
|
|
83
84
|
end
|
84
85
|
end
|
85
86
|
|
86
|
-
|
87
|
-
|
88
|
-
let(:books) { [Book.new(author_name: "foo", title: "bar")] }
|
87
|
+
context "with virtual columns" do
|
88
|
+
let(:books) { [Book.new(author_name: "foo", title: "bar")] }
|
89
89
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
90
|
+
it "ignores virtual columns and creates record" do
|
91
|
+
assert_difference "Book.count", +1 do
|
92
|
+
Book.import books
|
94
93
|
end
|
95
94
|
end
|
96
95
|
end
|
@@ -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
|
|
@@ -37,10 +38,8 @@ def should_support_postgresql_import_functionality
|
|
37
38
|
assert !topic.changed?
|
38
39
|
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
assert topic.previous_changes.present?
|
43
|
-
end
|
41
|
+
it "moves the dirty changes to previous_changes" do
|
42
|
+
assert topic.previous_changes.present?
|
44
43
|
end
|
45
44
|
|
46
45
|
it "marks models as persisted" do
|
@@ -95,13 +94,9 @@ def should_support_postgresql_import_functionality
|
|
95
94
|
describe "returning" do
|
96
95
|
let(:books) { [Book.new(author_name: "King", title: "It")] }
|
97
96
|
let(:result) { Book.import(books, returning: %w(author_name title)) }
|
98
|
-
let(:book_id)
|
99
|
-
|
100
|
-
|
101
|
-
else
|
102
|
-
books.first.id.to_s
|
103
|
-
end
|
104
|
-
end
|
97
|
+
let(:book_id) { books.first.id }
|
98
|
+
let(:true_returning_value) { true }
|
99
|
+
let(:false_returning_value) { false }
|
105
100
|
|
106
101
|
it "creates records" do
|
107
102
|
assert_difference("Book.count", +1) { result }
|
@@ -127,6 +122,15 @@ def should_support_postgresql_import_functionality
|
|
127
122
|
end
|
128
123
|
end
|
129
124
|
|
125
|
+
context "when a returning column is a serialized attribute" do
|
126
|
+
let(:vendor) { Vendor.new(hours: { monday: '8-5' }) }
|
127
|
+
let(:result) { Vendor.import([vendor], returning: %w(hours)) }
|
128
|
+
|
129
|
+
it "creates records" do
|
130
|
+
assert_difference("Vendor.count", +1) { result }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
130
134
|
context "when primary key and returning overlap" do
|
131
135
|
let(:result) { Book.import(books, returning: %w(id title)) }
|
132
136
|
|
@@ -141,6 +145,34 @@ def should_support_postgresql_import_functionality
|
|
141
145
|
end
|
142
146
|
end
|
143
147
|
|
148
|
+
context "when returning is raw sql" do
|
149
|
+
let(:result) { Book.import(books, returning: "title, (xmax = '0') AS inserted") }
|
150
|
+
|
151
|
+
setup { result }
|
152
|
+
|
153
|
+
it "returns ids" do
|
154
|
+
assert_equal [book_id], result.ids
|
155
|
+
end
|
156
|
+
|
157
|
+
it "returns specified columns" do
|
158
|
+
assert_equal [['It', true_returning_value]], result.results
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "when returning contains raw sql" do
|
163
|
+
let(:result) { Book.import(books, returning: [:title, "id, (xmax = '0') AS inserted"]) }
|
164
|
+
|
165
|
+
setup { result }
|
166
|
+
|
167
|
+
it "returns ids" do
|
168
|
+
assert_equal [book_id], result.ids
|
169
|
+
end
|
170
|
+
|
171
|
+
it "returns specified columns" do
|
172
|
+
assert_equal [['It', book_id, true_returning_value]], result.results
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
144
176
|
context "setting model attributes" do
|
145
177
|
let(:code) { 'abc' }
|
146
178
|
let(:discount) { 0.10 }
|
@@ -170,27 +202,33 @@ def should_support_postgresql_import_functionality
|
|
170
202
|
assert_equal updated_promotion.discount, discount
|
171
203
|
end
|
172
204
|
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
205
|
|
177
|
-
|
178
|
-
|
179
|
-
let(:vendor) { Vendor.new(name: "foo") }
|
180
|
-
let(:vendors) { [vendor] }
|
206
|
+
context 'returning raw sql' do
|
207
|
+
let(:returning_columns) { [:discount, "(xmax = '0') AS inserted"] }
|
181
208
|
|
182
|
-
|
183
|
-
|
184
|
-
|
209
|
+
it "sets custom model attributes" do
|
210
|
+
assert_equal updated_promotion.inserted, false_returning_value
|
211
|
+
end
|
185
212
|
end
|
186
213
|
end
|
214
|
+
end
|
215
|
+
end
|
187
216
|
|
188
|
-
|
217
|
+
describe "with a uuid primary key" do
|
218
|
+
let(:vendor) { Vendor.new(name: "foo") }
|
219
|
+
let(:vendors) { [vendor] }
|
220
|
+
|
221
|
+
it "creates records" do
|
222
|
+
assert_difference "Vendor.count", +1 do
|
189
223
|
Vendor.import vendors
|
190
|
-
assert_not_nil vendor.id
|
191
224
|
end
|
192
225
|
end
|
193
226
|
|
227
|
+
it "assigns an id to the model objects" do
|
228
|
+
Vendor.import vendors
|
229
|
+
assert_not_nil vendor.id
|
230
|
+
end
|
231
|
+
|
194
232
|
describe "with an assigned uuid primary key" do
|
195
233
|
let(:id) { SecureRandom.uuid }
|
196
234
|
let(:vendor) { Vendor.new(id: id, name: "foo") }
|
@@ -206,44 +244,38 @@ def should_support_postgresql_import_functionality
|
|
206
244
|
end
|
207
245
|
|
208
246
|
describe "with store accessor fields" do
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
Vendor.import vendors
|
214
|
-
end
|
215
|
-
assert_equal(100, Vendor.first.size)
|
247
|
+
it "imports values for json fields" do
|
248
|
+
vendors = [Vendor.new(name: 'Vendor 1', size: 100)]
|
249
|
+
assert_difference "Vendor.count", +1 do
|
250
|
+
Vendor.import vendors
|
216
251
|
end
|
252
|
+
assert_equal(100, Vendor.first.size)
|
253
|
+
end
|
217
254
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
end
|
223
|
-
assert_equal('John Smith', Vendor.first.contact)
|
255
|
+
it "imports values for hstore fields" do
|
256
|
+
vendors = [Vendor.new(name: 'Vendor 1', contact: 'John Smith')]
|
257
|
+
assert_difference "Vendor.count", +1 do
|
258
|
+
Vendor.import vendors
|
224
259
|
end
|
260
|
+
assert_equal('John Smith', Vendor.first.contact)
|
225
261
|
end
|
226
262
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
Vendor.import vendors
|
232
|
-
end
|
233
|
-
assert_equal('12345', Vendor.first.charge_code)
|
263
|
+
it "imports values for jsonb fields" do
|
264
|
+
vendors = [Vendor.new(name: 'Vendor 1', charge_code: '12345')]
|
265
|
+
assert_difference "Vendor.count", +1 do
|
266
|
+
Vendor.import vendors
|
234
267
|
end
|
268
|
+
assert_equal('12345', Vendor.first.charge_code)
|
235
269
|
end
|
236
270
|
end
|
237
271
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
Vendor.import vendors
|
244
|
-
end
|
245
|
-
assert_equal({}, Vendor.first.json_data)
|
272
|
+
describe "with serializable fields" do
|
273
|
+
it "imports default values as correct data type" do
|
274
|
+
vendors = [Vendor.new(name: 'Vendor 1')]
|
275
|
+
assert_difference "Vendor.count", +1 do
|
276
|
+
Vendor.import vendors
|
246
277
|
end
|
278
|
+
assert_equal({}, Vendor.first.json_data)
|
247
279
|
end
|
248
280
|
|
249
281
|
%w(json jsonb).each do |json_type|
|
@@ -272,7 +304,7 @@ def should_support_postgresql_import_functionality
|
|
272
304
|
end
|
273
305
|
|
274
306
|
describe "with binary field" do
|
275
|
-
let(:binary_value) { "\xE0'c\xB2\xB0\xB3Bh\\\xC2M\xB1m\\I\xC4r".force_encoding('ASCII-8BIT') }
|
307
|
+
let(:binary_value) { "\xE0'c\xB2\xB0\xB3Bh\\\xC2M\xB1m\\I\xC4r".dup.force_encoding('ASCII-8BIT') }
|
276
308
|
it "imports the correct values for binary fields" do
|
277
309
|
alarms = [Alarm.new(device_id: 1, alarm_type: 1, status: 1, secret_key: binary_value)]
|
278
310
|
assert_difference "Alarm.count", +1 do
|
@@ -281,6 +313,42 @@ def should_support_postgresql_import_functionality
|
|
281
313
|
assert_equal(binary_value, Alarm.first.secret_key)
|
282
314
|
end
|
283
315
|
end
|
316
|
+
|
317
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
318
|
+
describe "with composite foreign keys" do
|
319
|
+
let(:account_id) { 555 }
|
320
|
+
let(:customer) { Customer.new(account_id: account_id, name: "foo") }
|
321
|
+
let(:order) { Order.new(account_id: account_id, amount: 100, customer: customer) }
|
322
|
+
|
323
|
+
it "imports and correctly maps foreign keys" do
|
324
|
+
assert_difference "Customer.count", +1 do
|
325
|
+
Customer.import [customer]
|
326
|
+
end
|
327
|
+
|
328
|
+
assert_difference "Order.count", +1 do
|
329
|
+
Order.import [order]
|
330
|
+
end
|
331
|
+
|
332
|
+
db_customer = Customer.last
|
333
|
+
db_order = Order.last
|
334
|
+
|
335
|
+
assert_equal db_customer.orders.last, db_order
|
336
|
+
assert_not_equal db_order.customer_id, nil
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should import models with auto-incrementing ID successfully" do
|
340
|
+
author = Author.create!(name: "Foo Barson")
|
341
|
+
|
342
|
+
books = []
|
343
|
+
2.times do |i|
|
344
|
+
books << CompositeBook.new(author_id: author.id, title: "book #{i}")
|
345
|
+
end
|
346
|
+
assert_difference "CompositeBook.count", +2 do
|
347
|
+
CompositeBook.import books
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
284
352
|
end
|
285
353
|
|
286
354
|
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,7 +72,7 @@ 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
|
76
78
|
updated_values2 = User.all.map do |user|
|
@@ -80,7 +82,7 @@ def should_support_basic_on_duplicate_key_update
|
|
80
82
|
User.import(updated_values2, on_duplicate_key_update: [:name])
|
81
83
|
assert User.count == updated_values2.length
|
82
84
|
User.all.each_with_index do |user, i|
|
83
|
-
assert_equal user.name, users[i].name
|
85
|
+
assert_equal user.name, "#{users[i].name} Rothschild jr."
|
84
86
|
assert_equal 2, user.lock_version
|
85
87
|
end
|
86
88
|
end
|
@@ -102,7 +104,7 @@ def should_support_basic_on_duplicate_key_update
|
|
102
104
|
Account.import(updated_accounts, on_duplicate_key_update: [:id, :name])
|
103
105
|
assert Account.count == updated_accounts.length
|
104
106
|
Account.all.each_with_index do |user, i|
|
105
|
-
assert_equal user.name, accounts[i].name
|
107
|
+
assert_equal user.name, "#{accounts[i].name} Rothschild"
|
106
108
|
assert_equal 1, user.lock
|
107
109
|
end
|
108
110
|
end
|
@@ -126,7 +128,7 @@ def should_support_basic_on_duplicate_key_update
|
|
126
128
|
Account.import(columns, updated_values, on_duplicate_key_update: [:name])
|
127
129
|
assert Account.count == updated_values.length
|
128
130
|
Account.all.each_with_index do |user, i|
|
129
|
-
assert_equal user.name, accounts[i].name
|
131
|
+
assert_equal user.name, "#{accounts[i].name} Rothschild"
|
130
132
|
assert_equal 1, user.lock
|
131
133
|
end
|
132
134
|
end
|
@@ -148,7 +150,7 @@ def should_support_basic_on_duplicate_key_update
|
|
148
150
|
Account.import(updated_values, on_duplicate_key_update: [:name])
|
149
151
|
assert Account.count == updated_values.length
|
150
152
|
Account.all.each_with_index do |user, i|
|
151
|
-
assert_equal user.name, accounts[i].name
|
153
|
+
assert_equal user.name, "#{accounts[i].name} Rothschild"
|
152
154
|
assert_equal 1, user.lock
|
153
155
|
end
|
154
156
|
end
|
@@ -170,10 +172,11 @@ def should_support_basic_on_duplicate_key_update
|
|
170
172
|
Bike::Maker.import(updated_makers, on_duplicate_key_update: [:name])
|
171
173
|
assert Bike::Maker.count == updated_makers.length
|
172
174
|
Bike::Maker.all.each_with_index do |maker, i|
|
173
|
-
assert_equal maker.name, makers[i].name
|
175
|
+
assert_equal maker.name, "#{makers[i].name} bikes"
|
174
176
|
assert_equal 1, maker.lock_version
|
175
177
|
end
|
176
178
|
end
|
179
|
+
|
177
180
|
it 'update the lock_version of models separated by namespaces by array' do
|
178
181
|
makers = [
|
179
182
|
Bike::Maker.new(name: 'Yamaha'),
|
@@ -193,7 +196,7 @@ def should_support_basic_on_duplicate_key_update
|
|
193
196
|
Bike::Maker.import(columns, updated_values, on_duplicate_key_update: [:name])
|
194
197
|
assert Bike::Maker.count == updated_values.length
|
195
198
|
Bike::Maker.all.each_with_index do |maker, i|
|
196
|
-
assert_equal maker.name, makers[i].name
|
199
|
+
assert_equal maker.name, "#{makers[i].name} bikes"
|
197
200
|
assert_equal 1, maker.lock_version
|
198
201
|
end
|
199
202
|
end
|
@@ -215,11 +218,39 @@ def should_support_basic_on_duplicate_key_update
|
|
215
218
|
Bike::Maker.import(updated_values, on_duplicate_key_update: [:name])
|
216
219
|
assert Bike::Maker.count == updated_values.length
|
217
220
|
Bike::Maker.all.each_with_index do |maker, i|
|
218
|
-
assert_equal maker.name, makers[i].name
|
221
|
+
assert_equal maker.name, "#{makers[i].name} bikes"
|
219
222
|
assert_equal 1, maker.lock_version
|
220
223
|
end
|
221
224
|
end
|
222
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
|
223
254
|
end
|
224
255
|
|
225
256
|
context "with :on_duplicate_key_update" do
|
@@ -314,6 +345,34 @@ def should_support_basic_on_duplicate_key_update
|
|
314
345
|
should_support_on_duplicate_key_update
|
315
346
|
should_update_fields_mentioned
|
316
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
|
317
376
|
end
|
318
377
|
|
319
378
|
context "with a table that has a non-standard primary key" do
|