activerecord-import 0.16.2 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/.travis.yml +1 -0
- data/CHANGELOG.md +17 -1
- data/Gemfile +1 -0
- data/Rakefile +1 -0
- data/benchmarks/benchmark.rb +1 -0
- data/gemfiles/3.2.gemfile +2 -3
- data/gemfiles/4.0.gemfile +2 -3
- data/gemfiles/4.1.gemfile +2 -3
- data/gemfiles/4.2.gemfile +2 -7
- data/gemfiles/5.0.gemfile +2 -3
- data/lib/activerecord-import/active_record/adapters/jdbcsqlite3_adapter.rb +6 -0
- data/lib/activerecord-import/adapters/mysql_adapter.rb +1 -1
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +16 -3
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +2 -16
- data/lib/activerecord-import/import.rb +43 -8
- data/lib/activerecord-import/value_sets_parser.rb +11 -3
- data/lib/activerecord-import/version.rb +1 -1
- data/test/adapters/jdbcsqlite3.rb +1 -0
- data/test/import_test.rb +40 -0
- data/test/jdbcmysql/import_test.rb +0 -1
- data/test/jdbcpostgresql/import_test.rb +0 -1
- data/test/jdbcsqlite3/import_test.rb +4 -0
- data/test/models/book.rb +2 -0
- data/test/models/dictionary.rb +2 -0
- data/test/models/tag.rb +4 -0
- data/test/postgresql/import_test.rb +0 -4
- data/test/schema/generic_schema.rb +11 -0
- data/test/sqlite3/import_test.rb +2 -65
- data/test/support/postgresql/import_examples.rb +4 -0
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +12 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +12 -0
- data/test/support/sqlite3/import_examples.rb +67 -0
- data/test/test_helper.rb +1 -0
- data/test/travis/database.yml +4 -0
- metadata +22 -13
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
MDRjY2QxYTc1NmViOGI5MDM1Yjg5ODY1YmY3ZjU0ZGJkMGRiOTFhZA==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8a9d274345964888ca0c5b9242a689d3953271c7
|
4
|
+
data.tar.gz: 59dcdf278fec2582d2e44a7fb416d355026856a5
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ZTk2NGFkNjc1MjVkOGE2MGQyNTM2OTc1N2YzYjQyYmFlNjA0M2FhNjRiMDEz
|
11
|
-
YTgyNTk4MjIxOTUwYzYwOWM1ZmE5ZGQ0NDcyODkzNGNkNGYwMWU=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZmRlNTc0NjhhNmYxZmNiNDFjNzZlNTM1ODcxNTNiNzE3NmUyYjhjMjliYjU4
|
14
|
-
MGY5NmYyN2JhY2FjMmQ3ZWFiODE3ZTY3OTgxMDA5M2Y5MTkwZjIyNWU2Y2Ni
|
15
|
-
MWUyYTRlODNiMzg1YzYyNDEzMmFjZTBhZTEwMzFkZGU0MDI5ZTE=
|
6
|
+
metadata.gz: 96f341704f5a40797d626039985e52813d01087e9d41427ef375b5c383c1ce7f22cfac0120939e52ea037b86d84c8ae491ea098f82c2bcc6a94fea34a823dd02
|
7
|
+
data.tar.gz: 4d0f4890f4b97ea9c3d36bf8971d6334ab880d235eda6c14a2af7a8395900562fdb09fd2127c55d3283f48a8734e543671eb2ce0734b1f91689faa95a7bbff56
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## Changes in 0.17.0
|
2
|
+
|
3
|
+
### New Features
|
4
|
+
|
5
|
+
* Add support for composite_primary_keys gem. Thanks to @jkowens
|
6
|
+
via \#350.
|
7
|
+
* Add support for importing an array of hashes. Thanks to @jkowens
|
8
|
+
via \#352.
|
9
|
+
* Add JDBC SQLite3 support. Thanks to @jkowens via \#356.
|
10
|
+
|
11
|
+
### Fixes
|
12
|
+
|
13
|
+
* Remove support for SQLite recursive imports. See \#351.
|
14
|
+
* Improve import speed for Rails 5. Thanks to @ranchodeluxe, @jkowens
|
15
|
+
via \#359.
|
16
|
+
|
1
17
|
## Changes in 0.16.2
|
2
18
|
|
3
19
|
### Fixes
|
@@ -28,7 +44,7 @@
|
|
28
44
|
* Add store accessor support for JSON, JSON, and HSTORE data types.
|
29
45
|
Thanks to @jkowens via \#322
|
30
46
|
* Log warning if database does not support :on_duplicate_key_update.
|
31
|
-
Thanks to @jkowens
|
47
|
+
Thanks to @jkowens via \#324
|
32
48
|
* Add option :on_duplicate_key_ignore for MySQL and SQLite. Thanks to
|
33
49
|
@jkowens via \#326
|
34
50
|
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
data/benchmarks/benchmark.rb
CHANGED
data/gemfiles/3.2.gemfile
CHANGED
@@ -1,3 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
end
|
1
|
+
gem 'activerecord', '~> 3.2.0'
|
2
|
+
gem 'composite_primary_keys', '~> 5.0'
|
data/gemfiles/4.0.gemfile
CHANGED
@@ -1,3 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
end
|
1
|
+
gem 'activerecord', '~> 4.0.0'
|
2
|
+
gem 'composite_primary_keys', '~> 6.0'
|
data/gemfiles/4.1.gemfile
CHANGED
@@ -1,3 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
end
|
1
|
+
gem 'activerecord', '~> 4.1.0'
|
2
|
+
gem 'composite_primary_keys', '~> 7.0'
|
data/gemfiles/4.2.gemfile
CHANGED
data/gemfiles/5.0.gemfile
CHANGED
@@ -1,3 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
end
|
1
|
+
gem 'activerecord', '~> 5.0.0'
|
2
|
+
gem 'composite_primary_keys', '~> 9.0'
|
@@ -31,7 +31,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
31
31
|
max = max_allowed_packet
|
32
32
|
|
33
33
|
# if we can insert it all as one statement
|
34
|
-
if NO_MAX_PACKET == max || total_bytes
|
34
|
+
if NO_MAX_PACKET == max || total_bytes <= max
|
35
35
|
number_of_inserts += 1
|
36
36
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
37
37
|
insert( sql2insert, *args )
|
@@ -44,7 +44,8 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
44
44
|
sql += super(table_name, options)
|
45
45
|
|
46
46
|
unless options[:no_returning] || options[:primary_key].blank?
|
47
|
-
|
47
|
+
primary_key = Array(options[:primary_key])
|
48
|
+
sql << " RETURNING (#{primary_key.join(', ')})"
|
48
49
|
end
|
49
50
|
|
50
51
|
sql
|
@@ -136,8 +137,20 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
136
137
|
end
|
137
138
|
|
138
139
|
def sql_for_default_conflict_target( table_name )
|
139
|
-
|
140
|
-
|
140
|
+
pks = select_values(<<-SQL.strip_heredoc, "SCHEMA")
|
141
|
+
WITH pk_constraint AS (
|
142
|
+
SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
|
143
|
+
WHERE contype = 'p'
|
144
|
+
AND conrelid = #{quote(quote_table_name(table_name))}::regclass
|
145
|
+
), cons AS (
|
146
|
+
SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
|
147
|
+
)
|
148
|
+
SELECT attr.attname FROM pg_attribute attr
|
149
|
+
INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
|
150
|
+
ORDER BY cons.rownum
|
151
|
+
SQL
|
152
|
+
conflict_target = pks.join(', ')
|
153
|
+
"(#{conflict_target}) " if conflict_target.present?
|
141
154
|
end
|
142
155
|
|
143
156
|
# Return true if the statement is a duplicate key record error
|
@@ -19,7 +19,6 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
19
19
|
# elements that are in position >= 1 will be appended to the final SQL.
|
20
20
|
def insert_many(sql, values, *args) # :nodoc:
|
21
21
|
number_of_inserts = 0
|
22
|
-
ids = []
|
23
22
|
|
24
23
|
base_sql, post_sql = if sql.is_a?( String )
|
25
24
|
[sql, '']
|
@@ -34,15 +33,11 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
34
33
|
value_sets.each do |value_set|
|
35
34
|
number_of_inserts += 1
|
36
35
|
sql2insert = base_sql + value_set.join( ',' ) + post_sql
|
37
|
-
|
38
|
-
if last_insert_id > 0
|
39
|
-
first_insert_id = last_insert_id - affected_rows + 1
|
40
|
-
ids.concat((first_insert_id..last_insert_id).to_a)
|
41
|
-
end
|
36
|
+
insert( sql2insert, *args )
|
42
37
|
end
|
43
38
|
end
|
44
39
|
|
45
|
-
[number_of_inserts,
|
40
|
+
[number_of_inserts, []]
|
46
41
|
end
|
47
42
|
|
48
43
|
def pre_sql_statements( options)
|
@@ -57,13 +52,4 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
57
52
|
def next_value_for_sequence(sequence_name)
|
58
53
|
%{nextval('#{sequence_name}')}
|
59
54
|
end
|
60
|
-
|
61
|
-
def affected_rows
|
62
|
-
result = execute('SELECT changes();')
|
63
|
-
result.first[0]
|
64
|
-
end
|
65
|
-
|
66
|
-
def support_setting_primary_key_of_imported_objects?
|
67
|
-
true
|
68
|
-
end
|
69
55
|
end
|
@@ -144,6 +144,9 @@ class ActiveRecord::Base
|
|
144
144
|
#
|
145
145
|
# == Usage
|
146
146
|
# Model.import array_of_models
|
147
|
+
# Model.import column_names, array_of_models
|
148
|
+
# Model.import array_of_hash_objects
|
149
|
+
# Model.import column_names, array_of_hash_objects
|
147
150
|
# Model.import column_names, array_of_values
|
148
151
|
# Model.import column_names, array_of_values, options
|
149
152
|
#
|
@@ -187,7 +190,7 @@ class ActiveRecord::Base
|
|
187
190
|
# (if false) even if record timestamps is disabled in ActiveRecord::Base
|
188
191
|
# * +recursive+ - true|false, tells import to import all has_many/has_one
|
189
192
|
# associations if the adapter supports setting the primary keys of the
|
190
|
-
# newly imported objects.
|
193
|
+
# newly imported objects. PostgreSQL only.
|
191
194
|
# * +batch_size+ - an integer value to specify the max number of records to
|
192
195
|
# include per insert. Defaults to the total number of records to import.
|
193
196
|
#
|
@@ -200,6 +203,15 @@ class ActiveRecord::Base
|
|
200
203
|
# BlogPost.new author_name: 'Zach Dennis', title: 'AREXT3' ]
|
201
204
|
# BlogPost.import posts
|
202
205
|
#
|
206
|
+
# # Example using array_of_hash_objects
|
207
|
+
# values = [ {author_name: 'zdennis', title: 'test post'} ], [ {author_name: 'jdoe', title: 'another test post'} ] ]
|
208
|
+
# BlogPost.import values
|
209
|
+
#
|
210
|
+
# # Example using column_names and array_of_hash_objects
|
211
|
+
# columns = [ :author_name, :title ]
|
212
|
+
# values = [ {author_name: 'zdennis', title: 'test post'} ], [ {author_name: 'jdoe', title: 'another test post'} ] ]
|
213
|
+
# BlogPost.import columns, values
|
214
|
+
#
|
203
215
|
# # Example using column_names and array_of_values
|
204
216
|
# columns = [ :author_name, :title ]
|
205
217
|
# values = [ [ 'zdennis', 'test post' ], [ 'jdoe', 'another test post' ] ]
|
@@ -382,6 +394,21 @@ class ActiveRecord::Base
|
|
382
394
|
end
|
383
395
|
end
|
384
396
|
end
|
397
|
+
# supports array of hash objects
|
398
|
+
elsif args.last.is_a?( Array ) && args.last.first.is_a?(Hash)
|
399
|
+
if args.length == 2
|
400
|
+
array_of_hashes = args.last
|
401
|
+
column_names = args.first.dup
|
402
|
+
else
|
403
|
+
array_of_hashes = args.first
|
404
|
+
column_names = array_of_hashes.first.keys
|
405
|
+
end
|
406
|
+
|
407
|
+
array_of_attributes = array_of_hashes.map do |h|
|
408
|
+
column_names.map do |key|
|
409
|
+
h[key]
|
410
|
+
end
|
411
|
+
end
|
385
412
|
# supports empty array
|
386
413
|
elsif args.last.is_a?( Array ) && args.last.empty?
|
387
414
|
return ActiveRecord::Import::Result.new([], 0, [])
|
@@ -404,9 +431,15 @@ class ActiveRecord::Base
|
|
404
431
|
# Force the primary key col into the insert if it's not
|
405
432
|
# on the list and we are using a sequence and stuff a nil
|
406
433
|
# value for it into each row so the sequencer will fire later
|
407
|
-
|
408
|
-
|
409
|
-
|
434
|
+
symbolized_column_names = Array(column_names).map(&:to_sym)
|
435
|
+
symbolized_primary_key = Array(primary_key).map(&:to_sym)
|
436
|
+
|
437
|
+
if !symbolized_primary_key.to_set.subset?(symbolized_column_names.to_set) && connection.prefetch_primary_key? && sequence_name
|
438
|
+
column_count = column_names.size
|
439
|
+
column_names.concat(primary_key).uniq!
|
440
|
+
columns_added = column_names.size - column_count
|
441
|
+
new_fields = Array.new(columns_added)
|
442
|
+
array_of_attributes.each { |a| a.concat(new_fields) }
|
410
443
|
end
|
411
444
|
|
412
445
|
# record timestamps unless disabled in ActiveRecord::Base
|
@@ -626,9 +659,11 @@ class ActiveRecord::Base
|
|
626
659
|
# Returns SQL the VALUES for an INSERT statement given the passed in +columns+
|
627
660
|
# and +array_of_attributes+.
|
628
661
|
def values_sql_for_columns_and_attributes(columns, array_of_attributes) # :nodoc:
|
629
|
-
# connection
|
630
|
-
# Reuse the same
|
662
|
+
# connection and type_caster get called a *lot* in this high intensity loop.
|
663
|
+
# Reuse the same ones w/in the loop, otherwise they would keep being re-retreived (= lots of time for large imports)
|
631
664
|
connection_memo = connection
|
665
|
+
type_caster_memo = type_caster if respond_to?(:type_caster)
|
666
|
+
|
632
667
|
array_of_attributes.map do |arr|
|
633
668
|
my_values = arr.each_with_index.map do |val, j|
|
634
669
|
column = columns[j]
|
@@ -637,8 +672,8 @@ class ActiveRecord::Base
|
|
637
672
|
if val.nil? && column.name == primary_key && !sequence_name.blank?
|
638
673
|
connection_memo.next_value_for_sequence(sequence_name)
|
639
674
|
elsif column
|
640
|
-
if
|
641
|
-
connection_memo.quote(
|
675
|
+
if defined?(type_caster_memo) && type_caster_memo.respond_to?(:type_cast_for_database) # Rails 5.0 and higher
|
676
|
+
connection_memo.quote(type_caster_memo.type_cast_for_database(column.name, val))
|
642
677
|
elsif column.respond_to?(:type_cast_from_user) # Rails 4.2 and higher
|
643
678
|
connection_memo.quote(column.type_cast_from_user(val), column)
|
644
679
|
else # Rails 3.2, 4.0 and 4.1
|
@@ -8,8 +8,8 @@ module ActiveRecord::Import
|
|
8
8
|
|
9
9
|
def initialize(values, options)
|
10
10
|
@values = values
|
11
|
-
@reserved_bytes = options[:reserved_bytes]
|
12
|
-
@max_bytes = options
|
11
|
+
@reserved_bytes = options[:reserved_bytes] || 0
|
12
|
+
@max_bytes = options.fetch(:max_bytes) { default_max_bytes }
|
13
13
|
end
|
14
14
|
|
15
15
|
def parse
|
@@ -32,7 +32,15 @@ module ActiveRecord::Import
|
|
32
32
|
value_sets << arr if i == (values.size - 1)
|
33
33
|
end
|
34
34
|
|
35
|
-
|
35
|
+
value_sets
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def default_max_bytes
|
41
|
+
values_in_bytes = values.sum(&:bytesize)
|
42
|
+
comma_separated_bytes = values.size - 1
|
43
|
+
reserved_bytes + values_in_bytes + comma_separated_bytes
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
@@ -0,0 +1 @@
|
|
1
|
+
ENV["ARE_DB"] = "jdbcsqlite3"
|
data/test/import_test.rb
CHANGED
@@ -59,6 +59,46 @@ describe "#import" do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
describe "with an array of hashes" do
|
63
|
+
let(:columns) { [:title, :author_name] }
|
64
|
+
let(:values) { [{ title: "LDAP", author_name: "Jerry Carter", author_email_address: "jcarter@test.com" }, { title: "Rails Recipes", author_name: "Chad Fowler", author_email_address: "cfowler@test.com" }] }
|
65
|
+
|
66
|
+
it "should import hash data successfully" do
|
67
|
+
assert_difference "Topic.count", +2 do
|
68
|
+
Topic.import values, validate: false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should import specified hash data successfully" do
|
73
|
+
assert_difference "Topic.count", +2 do
|
74
|
+
Topic.import columns, values, validate: false
|
75
|
+
end
|
76
|
+
|
77
|
+
Topic.all.each do |t|
|
78
|
+
assert_nil t.author_email_address
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "with composite primary keys" do
|
84
|
+
it "should import models successfully" do
|
85
|
+
tags = [Tag.new(tag_id: 1, publisher_id: 1, tag: 'Mystery')]
|
86
|
+
|
87
|
+
assert_difference "Tag.count", +1 do
|
88
|
+
Tag.import tags
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should import array of values successfully" do
|
93
|
+
columns = [:tag_id, :publisher_id, :tag]
|
94
|
+
values = [[1, 1, 'Mystery'], [2, 1, 'Science']]
|
95
|
+
|
96
|
+
assert_difference "Tag.count", +2 do
|
97
|
+
Tag.import columns, values, validate: false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
62
102
|
describe "with STI models" do
|
63
103
|
it "should import models successfully" do
|
64
104
|
dictionaries = [Dictionary.new(author_name: "Noah Webster", title: "Webster's Dictionary")]
|
data/test/models/book.rb
CHANGED
data/test/models/dictionary.rb
CHANGED
data/test/models/tag.rb
ADDED
@@ -2,7 +2,3 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|
2
2
|
require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
|
3
3
|
|
4
4
|
should_support_postgresql_import_functionality
|
5
|
-
|
6
|
-
if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
|
7
|
-
should_support_postgresql_upsert_functionality
|
8
|
-
end
|
@@ -62,6 +62,8 @@ ActiveRecord::Schema.define do
|
|
62
62
|
t.datetime :updated_on
|
63
63
|
t.date :publish_date
|
64
64
|
t.integer :topic_id
|
65
|
+
t.integer :tag_id
|
66
|
+
t.integer :publisher_id
|
65
67
|
t.boolean :for_sale, default: true
|
66
68
|
t.integer :status, default: 0
|
67
69
|
t.string :type
|
@@ -151,4 +153,13 @@ ActiveRecord::Schema.define do
|
|
151
153
|
t.text :config
|
152
154
|
t.text :settings
|
153
155
|
end
|
156
|
+
|
157
|
+
execute %(
|
158
|
+
CREATE TABLE IF NOT EXISTS tags (
|
159
|
+
tag_id INT NOT NULL,
|
160
|
+
publisher_id INT NOT NULL,
|
161
|
+
tag VARCHAR(50),
|
162
|
+
PRIMARY KEY (tag_id, publisher_id)
|
163
|
+
);
|
164
|
+
).split.join(' ').strip
|
154
165
|
end
|
data/test/sqlite3/import_test.rb
CHANGED
@@ -1,67 +1,4 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../support/sqlite3/import_examples')
|
2
3
|
|
3
|
-
|
4
|
-
should_support_on_duplicate_key_ignore
|
5
|
-
|
6
|
-
describe "#supports_imports?" do
|
7
|
-
context "and SQLite is 3.7.11 or higher" do
|
8
|
-
it "supports import" do
|
9
|
-
version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.11")
|
10
|
-
assert ActiveRecord::Base.supports_import?(version)
|
11
|
-
|
12
|
-
version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.12")
|
13
|
-
assert ActiveRecord::Base.supports_import?(version)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
context "and SQLite less than 3.7.11" do
|
18
|
-
it "doesn't support import" do
|
19
|
-
version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.10")
|
20
|
-
assert !ActiveRecord::Base.supports_import?(version)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe "#import" do
|
26
|
-
it "imports with a single insert on SQLite 3.7.11 or higher" do
|
27
|
-
assert_difference "Topic.count", +507 do
|
28
|
-
result = Topic.import Build(7, :topics)
|
29
|
-
assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
30
|
-
assert_equal 7, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
31
|
-
|
32
|
-
result = Topic.import Build(500, :topics)
|
33
|
-
assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
34
|
-
assert_equal 507, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
it "imports with a two inserts on SQLite 3.7.11 or higher" do
|
39
|
-
assert_difference "Topic.count", +501 do
|
40
|
-
result = Topic.import Build(501, :topics)
|
41
|
-
assert_equal 2, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
42
|
-
assert_equal 501, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
it "imports with a five inserts on SQLite 3.7.11 or higher" do
|
47
|
-
assert_difference "Topic.count", +2500 do
|
48
|
-
result = Topic.import Build(2500, :topics)
|
49
|
-
assert_equal 5, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
50
|
-
assert_equal 2500, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context "with :on_duplicate_key_update" do
|
55
|
-
let(:topics) { Build(1, :topics) }
|
56
|
-
|
57
|
-
it "should log a warning message" do
|
58
|
-
log = StringIO.new
|
59
|
-
logger = Logger.new(log)
|
60
|
-
logger.level = Logger::WARN
|
61
|
-
ActiveRecord::Base.connection.stubs(:logger).returns(logger)
|
62
|
-
|
63
|
-
Topic.import topics, on_duplicate_key_update: true
|
64
|
-
assert_match(/Ignoring on_duplicate_key_update/, log.string)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
4
|
+
should_support_sqlite3_import_functionality
|
@@ -2,6 +2,10 @@
|
|
2
2
|
def should_support_postgresql_import_functionality
|
3
3
|
should_support_recursive_import
|
4
4
|
|
5
|
+
if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
|
6
|
+
should_support_postgresql_upsert_functionality
|
7
|
+
end
|
8
|
+
|
5
9
|
describe "#supports_imports?" do
|
6
10
|
it "should support import" do
|
7
11
|
assert ActiveRecord::Base.supports_import?
|
@@ -11,6 +11,18 @@ def should_support_on_duplicate_key_ignore
|
|
11
11
|
Topic.import topics, on_duplicate_key_ignore: true, validate: false
|
12
12
|
end
|
13
13
|
end
|
14
|
+
|
15
|
+
context "with composite primary keys" do
|
16
|
+
it "should import array of values successfully" do
|
17
|
+
columns = [:tag_id, :publisher_id, :tag]
|
18
|
+
values = [[1, 1, 'Mystery'], [1, 1, 'Science']]
|
19
|
+
|
20
|
+
assert_difference "Tag.count", +1 do
|
21
|
+
Tag.import columns, values, on_duplicate_key_ignore: true, validate: false
|
22
|
+
end
|
23
|
+
assert_equal 'Mystery', Tag.first.tag
|
24
|
+
end
|
25
|
+
end
|
14
26
|
end
|
15
27
|
|
16
28
|
context "with :ignore" do
|
@@ -76,6 +76,18 @@ def should_support_basic_on_duplicate_key_update
|
|
76
76
|
assert_equal 'DISCOUNT2', updated_promotion.code
|
77
77
|
end
|
78
78
|
end
|
79
|
+
|
80
|
+
context "with composite primary keys" do
|
81
|
+
it "should import array of values successfully" do
|
82
|
+
columns = [:tag_id, :publisher_id, :tag]
|
83
|
+
Tag.import columns, [[1, 1, 'Mystery']], validate: false
|
84
|
+
|
85
|
+
assert_difference "Tag.count", +0 do
|
86
|
+
Tag.import columns, [[1, 1, 'Science']], on_duplicate_key_update: [:tag], validate: false
|
87
|
+
end
|
88
|
+
assert_equal 'Science', Tag.first.tag
|
89
|
+
end
|
90
|
+
end
|
79
91
|
end
|
80
92
|
|
81
93
|
context "with :on_duplicate_key_update turned off" do
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
def should_support_sqlite3_import_functionality
|
3
|
+
should_support_on_duplicate_key_ignore
|
4
|
+
|
5
|
+
describe "#supports_imports?" do
|
6
|
+
context "and SQLite is 3.7.11 or higher" do
|
7
|
+
it "supports import" do
|
8
|
+
version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.11")
|
9
|
+
assert ActiveRecord::Base.supports_import?(version)
|
10
|
+
|
11
|
+
version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.12")
|
12
|
+
assert ActiveRecord::Base.supports_import?(version)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "and SQLite less than 3.7.11" do
|
17
|
+
it "doesn't support import" do
|
18
|
+
version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.10")
|
19
|
+
assert !ActiveRecord::Base.supports_import?(version)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#import" do
|
25
|
+
it "imports with a single insert on SQLite 3.7.11 or higher" do
|
26
|
+
assert_difference "Topic.count", +507 do
|
27
|
+
result = Topic.import Build(7, :topics)
|
28
|
+
assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
29
|
+
assert_equal 7, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
30
|
+
|
31
|
+
result = Topic.import Build(500, :topics)
|
32
|
+
assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
33
|
+
assert_equal 507, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "imports with a two inserts on SQLite 3.7.11 or higher" do
|
38
|
+
assert_difference "Topic.count", +501 do
|
39
|
+
result = Topic.import Build(501, :topics)
|
40
|
+
assert_equal 2, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
41
|
+
assert_equal 501, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "imports with a five inserts on SQLite 3.7.11 or higher" do
|
46
|
+
assert_difference "Topic.count", +2500 do
|
47
|
+
result = Topic.import Build(2500, :topics)
|
48
|
+
assert_equal 5, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
49
|
+
assert_equal 2500, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "with :on_duplicate_key_update" do
|
54
|
+
let(:topics) { Build(1, :topics) }
|
55
|
+
|
56
|
+
it "should log a warning message" do
|
57
|
+
log = StringIO.new
|
58
|
+
logger = Logger.new(log)
|
59
|
+
logger.level = Logger::WARN
|
60
|
+
ActiveRecord::Base.connection.stubs(:logger).returns(logger)
|
61
|
+
|
62
|
+
Topic.import topics, on_duplicate_key_update: true
|
63
|
+
assert_match(/Ignoring on_duplicate_key_update/, log.string)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/test/test_helper.rb
CHANGED
data/test/travis/database.yml
CHANGED
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-import
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.17.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Dennis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '3.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
description: Extraction of the ActiveRecord::Base#import functionality from ar-extensions
|
@@ -46,10 +46,10 @@ executables: []
|
|
46
46
|
extensions: []
|
47
47
|
extra_rdoc_files: []
|
48
48
|
files:
|
49
|
-
- .gitignore
|
50
|
-
- .rubocop.yml
|
51
|
-
- .rubocop_todo.yml
|
52
|
-
- .travis.yml
|
49
|
+
- ".gitignore"
|
50
|
+
- ".rubocop.yml"
|
51
|
+
- ".rubocop_todo.yml"
|
52
|
+
- ".travis.yml"
|
53
53
|
- Brewfile
|
54
54
|
- CHANGELOG.md
|
55
55
|
- Gemfile
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- lib/activerecord-import/active_record/adapters/abstract_adapter.rb
|
79
79
|
- lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb
|
80
80
|
- lib/activerecord-import/active_record/adapters/jdbcpostgresql_adapter.rb
|
81
|
+
- lib/activerecord-import/active_record/adapters/jdbcsqlite3_adapter.rb
|
81
82
|
- lib/activerecord-import/active_record/adapters/mysql2_adapter.rb
|
82
83
|
- lib/activerecord-import/active_record/adapters/postgresql_adapter.rb
|
83
84
|
- lib/activerecord-import/active_record/adapters/seamless_database_pool_adapter.rb
|
@@ -98,6 +99,7 @@ files:
|
|
98
99
|
- lib/activerecord-import/version.rb
|
99
100
|
- test/adapters/jdbcmysql.rb
|
100
101
|
- test/adapters/jdbcpostgresql.rb
|
102
|
+
- test/adapters/jdbcsqlite3.rb
|
101
103
|
- test/adapters/mysql2.rb
|
102
104
|
- test/adapters/mysql2_makara.rb
|
103
105
|
- test/adapters/mysql2spatial.rb
|
@@ -111,6 +113,7 @@ files:
|
|
111
113
|
- test/import_test.rb
|
112
114
|
- test/jdbcmysql/import_test.rb
|
113
115
|
- test/jdbcpostgresql/import_test.rb
|
116
|
+
- test/jdbcsqlite3/import_test.rb
|
114
117
|
- test/models/alarm.rb
|
115
118
|
- test/models/book.rb
|
116
119
|
- test/models/chapter.rb
|
@@ -121,6 +124,7 @@ files:
|
|
121
124
|
- test/models/promotion.rb
|
122
125
|
- test/models/question.rb
|
123
126
|
- test/models/rule.rb
|
127
|
+
- test/models/tag.rb
|
124
128
|
- test/models/topic.rb
|
125
129
|
- test/models/vendor.rb
|
126
130
|
- test/models/widget.rb
|
@@ -145,6 +149,7 @@ files:
|
|
145
149
|
- test/support/shared_examples/on_duplicate_key_ignore.rb
|
146
150
|
- test/support/shared_examples/on_duplicate_key_update.rb
|
147
151
|
- test/support/shared_examples/recursive_import.rb
|
152
|
+
- test/support/sqlite3/import_examples.rb
|
148
153
|
- test/synchronize_test.rb
|
149
154
|
- test/test_helper.rb
|
150
155
|
- test/travis/database.yml
|
@@ -160,23 +165,24 @@ require_paths:
|
|
160
165
|
- lib
|
161
166
|
required_ruby_version: !ruby/object:Gem::Requirement
|
162
167
|
requirements:
|
163
|
-
- -
|
168
|
+
- - ">="
|
164
169
|
- !ruby/object:Gem::Version
|
165
170
|
version: 1.9.2
|
166
171
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
172
|
requirements:
|
168
|
-
- -
|
173
|
+
- - ">="
|
169
174
|
- !ruby/object:Gem::Version
|
170
175
|
version: '0'
|
171
176
|
requirements: []
|
172
177
|
rubyforge_project:
|
173
|
-
rubygems_version: 2.
|
178
|
+
rubygems_version: 2.6.2
|
174
179
|
signing_key:
|
175
180
|
specification_version: 4
|
176
181
|
summary: Bulk-loading extension for ActiveRecord
|
177
182
|
test_files:
|
178
183
|
- test/adapters/jdbcmysql.rb
|
179
184
|
- test/adapters/jdbcpostgresql.rb
|
185
|
+
- test/adapters/jdbcsqlite3.rb
|
180
186
|
- test/adapters/mysql2.rb
|
181
187
|
- test/adapters/mysql2_makara.rb
|
182
188
|
- test/adapters/mysql2spatial.rb
|
@@ -190,6 +196,7 @@ test_files:
|
|
190
196
|
- test/import_test.rb
|
191
197
|
- test/jdbcmysql/import_test.rb
|
192
198
|
- test/jdbcpostgresql/import_test.rb
|
199
|
+
- test/jdbcsqlite3/import_test.rb
|
193
200
|
- test/models/alarm.rb
|
194
201
|
- test/models/book.rb
|
195
202
|
- test/models/chapter.rb
|
@@ -200,6 +207,7 @@ test_files:
|
|
200
207
|
- test/models/promotion.rb
|
201
208
|
- test/models/question.rb
|
202
209
|
- test/models/rule.rb
|
210
|
+
- test/models/tag.rb
|
203
211
|
- test/models/topic.rb
|
204
212
|
- test/models/vendor.rb
|
205
213
|
- test/models/widget.rb
|
@@ -224,6 +232,7 @@ test_files:
|
|
224
232
|
- test/support/shared_examples/on_duplicate_key_ignore.rb
|
225
233
|
- test/support/shared_examples/on_duplicate_key_update.rb
|
226
234
|
- test/support/shared_examples/recursive_import.rb
|
235
|
+
- test/support/sqlite3/import_examples.rb
|
227
236
|
- test/synchronize_test.rb
|
228
237
|
- test/test_helper.rb
|
229
238
|
- test/travis/database.yml
|