activerecord-import 0.22.0 → 1.4.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 +5 -5
- data/.github/workflows/test.yaml +107 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +74 -8
- data/Brewfile +3 -1
- data/CHANGELOG.md +235 -3
- data/Gemfile +22 -15
- data/LICENSE +21 -56
- data/README.markdown +574 -22
- data/Rakefile +4 -1
- data/activerecord-import.gemspec +6 -5
- data/benchmarks/benchmark.rb +7 -1
- data/benchmarks/lib/base.rb +2 -0
- data/benchmarks/lib/cli_parser.rb +3 -1
- 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/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 +4 -0
- data/gemfiles/6.0.gemfile +4 -0
- data/gemfiles/6.1.gemfile +4 -0
- data/gemfiles/7.0.gemfile +4 -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/adapters/abstract_adapter.rb +10 -2
- 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 +19 -11
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +56 -37
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +128 -9
- data/lib/activerecord-import/base.rb +12 -2
- data/lib/activerecord-import/import.rb +300 -136
- 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 +4 -2
- data/lib/activerecord-import/value_sets_parser.rb +4 -0
- data/lib/activerecord-import/version.rb +3 -1
- data/lib/activerecord-import.rb +4 -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 +3 -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/{travis → github}/database.yml +3 -1
- data/test/import_test.rb +159 -8
- data/test/jdbcmysql/import_test.rb +2 -0
- data/test/jdbcpostgresql/import_test.rb +2 -0
- data/test/jdbcsqlite3/import_test.rb +2 -0
- data/test/makara_postgis/import_test.rb +10 -0
- data/test/models/account.rb +5 -0
- data/test/models/alarm.rb +2 -0
- data/test/models/animal.rb +8 -0
- data/test/models/bike_maker.rb +9 -0
- data/test/models/book.rb +2 -0
- data/test/models/car.rb +2 -0
- data/test/models/card.rb +5 -0
- data/test/models/chapter.rb +2 -0
- data/test/models/customer.rb +8 -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 +8 -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 +3 -0
- data/test/models/tag_alias.rb +5 -0
- data/test/models/topic.rb +2 -0
- data/test/models/user.rb +5 -0
- data/test/models/user_token.rb +6 -0
- data/test/models/vendor.rb +2 -0
- data/test/models/widget.rb +2 -0
- data/test/mysql2/import_test.rb +2 -0
- data/test/mysql2_makara/import_test.rb +2 -0
- data/test/mysqlspatial2/import_test.rb +2 -0
- data/test/postgis/import_test.rb +2 -0
- data/test/postgresql/import_test.rb +2 -0
- data/test/schema/generic_schema.rb +53 -0
- data/test/schema/jdbcpostgresql_schema.rb +2 -0
- data/test/schema/mysql2_schema.rb +21 -0
- data/test/schema/postgis_schema.rb +2 -0
- data/test/schema/postgresql_schema.rb +18 -0
- data/test/schema/sqlite3_schema.rb +15 -0
- data/test/schema/version.rb +2 -0
- data/test/sqlite3/import_test.rb +2 -0
- data/test/support/active_support/test_case_extensions.rb +2 -0
- data/test/support/assertions.rb +2 -0
- data/test/support/factories.rb +10 -8
- data/test/support/generate.rb +10 -8
- data/test/support/mysql/import_examples.rb +14 -1
- data/test/support/postgresql/import_examples.rb +140 -3
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +2 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +263 -0
- data/test/support/shared_examples/recursive_import.rb +76 -4
- data/test/support/sqlite3/import_examples.rb +191 -26
- data/test/synchronize_test.rb +2 -0
- data/test/test_helper.rb +36 -3
- data/test/value_sets_bytes_parser_test.rb +2 -0
- data/test/value_sets_records_parser_test.rb +2 -0
- metadata +46 -18
- data/.travis.yml +0 -61
- data/gemfiles/3.2.gemfile +0 -2
- data/gemfiles/4.0.gemfile +0 -2
- data/gemfiles/4.1.gemfile +0 -2
- data/test/schema/mysql_schema.rb +0 -16
data/test/postgis/import_test.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
ActiveRecord::Schema.define do
|
|
2
4
|
create_table :schema_info, force: :cascade do |t|
|
|
3
5
|
t.integer :version, unique: true
|
|
@@ -52,6 +54,20 @@ ActiveRecord::Schema.define do
|
|
|
52
54
|
t.string :name
|
|
53
55
|
end
|
|
54
56
|
|
|
57
|
+
create_table :cards, force: :cascade do |t|
|
|
58
|
+
t.string :name
|
|
59
|
+
t.string :deck_type
|
|
60
|
+
t.integer :deck_id
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
create_table :decks, force: :cascade do |t|
|
|
64
|
+
t.string :name
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
create_table :playing_cards, force: :cascade do |t|
|
|
68
|
+
t.string :name
|
|
69
|
+
end
|
|
70
|
+
|
|
55
71
|
create_table :books, force: :cascade do |t|
|
|
56
72
|
t.string :title, null: false
|
|
57
73
|
t.string :publisher, null: false, default: 'Default Publisher'
|
|
@@ -159,6 +175,26 @@ ActiveRecord::Schema.define do
|
|
|
159
175
|
t.string :Features
|
|
160
176
|
end
|
|
161
177
|
|
|
178
|
+
create_table :users, force: :cascade do |t|
|
|
179
|
+
t.string :name, null: false
|
|
180
|
+
t.integer :lock_version, null: false, default: 0
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
create_table :user_tokens, force: :cascade do |t|
|
|
184
|
+
t.string :user_name, null: false
|
|
185
|
+
t.string :token, null: false
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
create_table :accounts, force: :cascade do |t|
|
|
189
|
+
t.string :name, null: false
|
|
190
|
+
t.integer :lock, null: false, default: 0
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
create_table :bike_makers, force: :cascade do |t|
|
|
194
|
+
t.string :name, null: false
|
|
195
|
+
t.integer :lock_version, null: false, default: 0
|
|
196
|
+
end
|
|
197
|
+
|
|
162
198
|
add_index :cars, :Name, unique: true
|
|
163
199
|
|
|
164
200
|
unless ENV["SKIP_COMPOSITE_PK"]
|
|
@@ -170,5 +206,22 @@ ActiveRecord::Schema.define do
|
|
|
170
206
|
PRIMARY KEY (tag_id, publisher_id)
|
|
171
207
|
);
|
|
172
208
|
).split.join(' ').strip
|
|
209
|
+
|
|
210
|
+
create_table :tag_aliases, force: :cascade do |t|
|
|
211
|
+
t.integer :tag_id, null: false
|
|
212
|
+
t.integer :parent_id, null: false
|
|
213
|
+
t.string :alias, null: false
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
create_table :customers, force: :cascade do |t|
|
|
218
|
+
t.integer :account_id
|
|
219
|
+
t.string :name
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
create_table :orders, force: :cascade do |t|
|
|
223
|
+
t.integer :account_id
|
|
224
|
+
t.integer :customer_id
|
|
225
|
+
t.integer :amount
|
|
173
226
|
end
|
|
174
227
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
ActiveRecord::Schema.define do
|
|
4
|
+
create_table :books, force: :cascade do |t|
|
|
5
|
+
t.string :title, null: false
|
|
6
|
+
t.virtual :upper_title, type: :string, as: "upper(`title`)" if t.respond_to?(:virtual)
|
|
7
|
+
t.string :publisher, null: false, default: 'Default Publisher'
|
|
8
|
+
t.string :author_name, null: false
|
|
9
|
+
t.datetime :created_at
|
|
10
|
+
t.datetime :created_on
|
|
11
|
+
t.datetime :updated_at
|
|
12
|
+
t.datetime :updated_on
|
|
13
|
+
t.date :publish_date
|
|
14
|
+
t.integer :topic_id
|
|
15
|
+
t.integer :tag_id
|
|
16
|
+
t.integer :publisher_id
|
|
17
|
+
t.boolean :for_sale, default: true
|
|
18
|
+
t.integer :status, default: 0
|
|
19
|
+
t.string :type
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -1,13 +1,28 @@
|
|
|
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 => e
|
|
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
|
|
15
|
+
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'wholesaler\';')
|
|
16
|
+
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'retailer\';')
|
|
17
|
+
end
|
|
18
|
+
|
|
6
19
|
create_table :vendors, id: :uuid, force: :cascade do |t|
|
|
7
20
|
t.string :name, null: true
|
|
21
|
+
t.text :hours
|
|
8
22
|
t.text :preferences
|
|
9
23
|
|
|
10
24
|
if t.respond_to?(:json)
|
|
25
|
+
t.json :pure_json_data
|
|
11
26
|
t.json :data
|
|
12
27
|
else
|
|
13
28
|
t.text :data
|
|
@@ -20,6 +35,7 @@ ActiveRecord::Schema.define do
|
|
|
20
35
|
end
|
|
21
36
|
|
|
22
37
|
if t.respond_to?(:jsonb)
|
|
38
|
+
t.jsonb :pure_jsonb_data
|
|
23
39
|
t.jsonb :settings
|
|
24
40
|
t.jsonb :json_data, null: false, default: {}
|
|
25
41
|
else
|
|
@@ -27,6 +43,8 @@ ActiveRecord::Schema.define do
|
|
|
27
43
|
t.text :json_data
|
|
28
44
|
end
|
|
29
45
|
|
|
46
|
+
t.column :vendor_type, :vendor_type
|
|
47
|
+
|
|
30
48
|
t.datetime :created_at
|
|
31
49
|
t.datetime :updated_at
|
|
32
50
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
ActiveRecord::Schema.define do
|
|
4
|
+
create_table :alarms, force: true do |t|
|
|
5
|
+
t.column :device_id, :integer, null: false
|
|
6
|
+
t.column :alarm_type, :integer, null: false
|
|
7
|
+
t.column :status, :integer, null: false
|
|
8
|
+
t.column :metadata, :text
|
|
9
|
+
t.column :secret_key, :binary
|
|
10
|
+
t.datetime :created_at
|
|
11
|
+
t.datetime :updated_at
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
add_index :alarms, [:device_id, :alarm_type], unique: true, where: 'status <> 0'
|
|
15
|
+
end
|
data/test/schema/version.rb
CHANGED
data/test/sqlite3/import_test.rb
CHANGED
data/test/support/assertions.rb
CHANGED
data/test/support/factories.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
FactoryBot.define do
|
|
2
4
|
sequence(:book_title) { |n| "Book #{n}" }
|
|
3
5
|
sequence(:chapter_title) { |n| "Chapter #{n}" }
|
|
4
6
|
sequence(:end_note) { |n| "Endnote #{n}" }
|
|
@@ -9,7 +11,7 @@ FactoryGirl.define do
|
|
|
9
11
|
|
|
10
12
|
factory :invalid_topic, class: "Topic" do
|
|
11
13
|
sequence(:title) { |n| "Title #{n}" }
|
|
12
|
-
author_name nil
|
|
14
|
+
author_name { nil }
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
factory :topic do
|
|
@@ -27,7 +29,7 @@ FactoryGirl.define do
|
|
|
27
29
|
|
|
28
30
|
trait :with_rule do
|
|
29
31
|
after(:build) do |question|
|
|
30
|
-
question.build_rule(
|
|
32
|
+
question.build_rule(FactoryBot.attributes_for(:rule))
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
@@ -40,21 +42,21 @@ FactoryGirl.define do
|
|
|
40
42
|
factory :topic_with_book, parent: :topic do
|
|
41
43
|
after(:build) do |topic|
|
|
42
44
|
2.times do
|
|
43
|
-
book = topic.books.build(title:
|
|
45
|
+
book = topic.books.build(title: FactoryBot.generate(:book_title), author_name: 'Stephen King')
|
|
44
46
|
3.times do
|
|
45
|
-
book.chapters.build(title:
|
|
47
|
+
book.chapters.build(title: FactoryBot.generate(:chapter_title))
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
4.times do
|
|
49
|
-
book.end_notes.build(note:
|
|
51
|
+
book.end_notes.build(note: FactoryBot.generate(:end_note))
|
|
50
52
|
end
|
|
51
53
|
end
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
factory :book do
|
|
56
|
-
title 'Tortilla Flat'
|
|
57
|
-
author_name 'John Steinbeck'
|
|
58
|
+
title { 'Tortilla Flat' }
|
|
59
|
+
author_name { 'John Steinbeck' }
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
factory :car do
|
data/test/support/generate.rb
CHANGED
|
@@ -1,29 +1,31 @@
|
|
|
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 || {}
|
|
6
8
|
|
|
7
9
|
if n
|
|
8
10
|
[].tap do |collection|
|
|
9
|
-
n.times.each { collection <<
|
|
11
|
+
n.times.each { collection << FactoryBot.build(factory.to_s.singularize.to_sym, factory_bot_args) }
|
|
10
12
|
end
|
|
11
13
|
else
|
|
12
|
-
|
|
14
|
+
FactoryBot.build(factory.to_s.singularize.to_sym, factory_bot_args)
|
|
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 || {}
|
|
20
22
|
|
|
21
23
|
if n
|
|
22
24
|
[].tap do |collection|
|
|
23
|
-
n.times.each { collection <<
|
|
25
|
+
n.times.each { collection << FactoryBot.create(factory.to_s.singularize.to_sym, factory_bot_args) }
|
|
24
26
|
end
|
|
25
27
|
else
|
|
26
|
-
|
|
28
|
+
FactoryBot.create(factory.to_s.singularize.to_sym, factory_bot_args)
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
end
|
|
@@ -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'"
|
|
@@ -82,5 +83,17 @@ def should_support_mysql_import_functionality
|
|
|
82
83
|
assert_equal "Chad Fowler", topics.last.author_name, "wrong author!"
|
|
83
84
|
end
|
|
84
85
|
end
|
|
86
|
+
|
|
87
|
+
if ENV['AR_VERSION'].to_f >= 5.1
|
|
88
|
+
context "with virtual columns" do
|
|
89
|
+
let(:books) { [Book.new(author_name: "foo", title: "bar")] }
|
|
90
|
+
|
|
91
|
+
it "ignores virtual columns and creates record" do
|
|
92
|
+
assert_difference "Book.count", +1 do
|
|
93
|
+
Book.import books
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
85
98
|
end
|
|
86
99
|
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,6 +38,12 @@ def should_support_postgresql_import_functionality
|
|
|
37
38
|
assert !topic.changed?
|
|
38
39
|
end
|
|
39
40
|
|
|
41
|
+
if ENV['AR_VERSION'].to_f > 4.1
|
|
42
|
+
it "moves the dirty changes to previous_changes" do
|
|
43
|
+
assert topic.previous_changes.present?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
40
47
|
it "marks models as persisted" do
|
|
41
48
|
assert !topic.new_record?
|
|
42
49
|
assert topic.persisted?
|
|
@@ -96,6 +103,8 @@ def should_support_postgresql_import_functionality
|
|
|
96
103
|
books.first.id.to_s
|
|
97
104
|
end
|
|
98
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' }
|
|
99
108
|
|
|
100
109
|
it "creates records" do
|
|
101
110
|
assert_difference("Book.count", +1) { result }
|
|
@@ -110,6 +119,26 @@ def should_support_postgresql_import_functionality
|
|
|
110
119
|
assert_equal [%w(King It)], result.results
|
|
111
120
|
end
|
|
112
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
|
+
|
|
113
142
|
context "when primary key and returning overlap" do
|
|
114
143
|
let(:result) { Book.import(books, returning: %w(id title)) }
|
|
115
144
|
|
|
@@ -124,6 +153,34 @@ def should_support_postgresql_import_functionality
|
|
|
124
153
|
end
|
|
125
154
|
end
|
|
126
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
|
+
|
|
127
184
|
context "setting model attributes" do
|
|
128
185
|
let(:code) { 'abc' }
|
|
129
186
|
let(:discount) { 0.10 }
|
|
@@ -153,6 +210,14 @@ def should_support_postgresql_import_functionality
|
|
|
153
210
|
assert_equal updated_promotion.discount, discount
|
|
154
211
|
end
|
|
155
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
|
|
156
221
|
end
|
|
157
222
|
end
|
|
158
223
|
end
|
|
@@ -228,10 +293,34 @@ def should_support_postgresql_import_functionality
|
|
|
228
293
|
assert_equal({}, Vendor.first.json_data)
|
|
229
294
|
end
|
|
230
295
|
end
|
|
296
|
+
|
|
297
|
+
%w(json jsonb).each do |json_type|
|
|
298
|
+
describe "with pure #{json_type} fields" do
|
|
299
|
+
let(:data) { { a: :b } }
|
|
300
|
+
let(:json_field_name) { "pure_#{json_type}_data" }
|
|
301
|
+
it "imports the values from saved records" do
|
|
302
|
+
vendor = Vendor.create!(name: 'Vendor 1', json_field_name => data)
|
|
303
|
+
|
|
304
|
+
Vendor.import [vendor], on_duplicate_key_update: [json_field_name]
|
|
305
|
+
assert_equal(data.as_json, vendor.reload[json_field_name])
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
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
|
|
231
320
|
end
|
|
232
321
|
|
|
233
322
|
describe "with binary field" do
|
|
234
|
-
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') }
|
|
235
324
|
it "imports the correct values for binary fields" do
|
|
236
325
|
alarms = [Alarm.new(device_id: 1, alarm_type: 1, status: 1, secret_key: binary_value)]
|
|
237
326
|
assert_difference "Alarm.count", +1 do
|
|
@@ -240,6 +329,30 @@ def should_support_postgresql_import_functionality
|
|
|
240
329
|
assert_equal(binary_value, Alarm.first.secret_key)
|
|
241
330
|
end
|
|
242
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
|
+
end
|
|
355
|
+
end
|
|
243
356
|
end
|
|
244
357
|
|
|
245
358
|
def should_support_postgresql_upsert_functionality
|
|
@@ -295,6 +408,30 @@ def should_support_postgresql_upsert_functionality
|
|
|
295
408
|
end
|
|
296
409
|
|
|
297
410
|
context "using a hash" do
|
|
411
|
+
context "with :columns :all" do
|
|
412
|
+
let(:columns) { %w( id title author_name author_email_address parent_id ) }
|
|
413
|
+
let(:updated_values) { [[99, "Book - 2nd Edition", "Jane Doe", "janedoe@example.com", 57]] }
|
|
414
|
+
|
|
415
|
+
macro(:perform_import) do |*opts|
|
|
416
|
+
Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: :id, columns: :all }, validate: false)
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
setup do
|
|
420
|
+
values = [[99, "Book", "John Doe", "john@doe.com", 17, 3]]
|
|
421
|
+
Topic.import columns + ['replies_count'], values, validate: false
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
it "should update all specified columns" do
|
|
425
|
+
perform_import
|
|
426
|
+
updated_topic = Topic.find(99)
|
|
427
|
+
assert_equal 'Book - 2nd Edition', updated_topic.title
|
|
428
|
+
assert_equal 'Jane Doe', updated_topic.author_name
|
|
429
|
+
assert_equal 'janedoe@example.com', updated_topic.author_email_address
|
|
430
|
+
assert_equal 57, updated_topic.parent_id
|
|
431
|
+
assert_equal 3, updated_topic.replies_count
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
298
435
|
context "with :columns a hash" do
|
|
299
436
|
let(:columns) { %w( id title author_name author_email_address parent_id ) }
|
|
300
437
|
let(:values) { [[99, "Book", "John Doe", "john@doe.com", 17]] }
|
|
@@ -312,7 +449,7 @@ def should_support_postgresql_upsert_functionality
|
|
|
312
449
|
it "should not modify the passed in :on_duplicate_key_update columns array" do
|
|
313
450
|
assert_nothing_raised do
|
|
314
451
|
columns = %w(title author_name).freeze
|
|
315
|
-
Topic.import columns, [%w(foo, bar)], on_duplicate_key_update: { columns: columns }
|
|
452
|
+
Topic.import columns, [%w(foo, bar)], { on_duplicate_key_update: { columns: columns }.freeze }.freeze
|
|
316
453
|
end
|
|
317
454
|
end
|
|
318
455
|
|