activerecord-import 0.16.2 → 0.17.0

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.
Files changed (36) hide show
  1. checksums.yaml +5 -13
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +17 -1
  4. data/Gemfile +1 -0
  5. data/Rakefile +1 -0
  6. data/benchmarks/benchmark.rb +1 -0
  7. data/gemfiles/3.2.gemfile +2 -3
  8. data/gemfiles/4.0.gemfile +2 -3
  9. data/gemfiles/4.1.gemfile +2 -3
  10. data/gemfiles/4.2.gemfile +2 -7
  11. data/gemfiles/5.0.gemfile +2 -3
  12. data/lib/activerecord-import/active_record/adapters/jdbcsqlite3_adapter.rb +6 -0
  13. data/lib/activerecord-import/adapters/mysql_adapter.rb +1 -1
  14. data/lib/activerecord-import/adapters/postgresql_adapter.rb +16 -3
  15. data/lib/activerecord-import/adapters/sqlite3_adapter.rb +2 -16
  16. data/lib/activerecord-import/import.rb +43 -8
  17. data/lib/activerecord-import/value_sets_parser.rb +11 -3
  18. data/lib/activerecord-import/version.rb +1 -1
  19. data/test/adapters/jdbcsqlite3.rb +1 -0
  20. data/test/import_test.rb +40 -0
  21. data/test/jdbcmysql/import_test.rb +0 -1
  22. data/test/jdbcpostgresql/import_test.rb +0 -1
  23. data/test/jdbcsqlite3/import_test.rb +4 -0
  24. data/test/models/book.rb +2 -0
  25. data/test/models/dictionary.rb +2 -0
  26. data/test/models/tag.rb +4 -0
  27. data/test/postgresql/import_test.rb +0 -4
  28. data/test/schema/generic_schema.rb +11 -0
  29. data/test/sqlite3/import_test.rb +2 -65
  30. data/test/support/postgresql/import_examples.rb +4 -0
  31. data/test/support/shared_examples/on_duplicate_key_ignore.rb +12 -0
  32. data/test/support/shared_examples/on_duplicate_key_update.rb +12 -0
  33. data/test/support/sqlite3/import_examples.rb +67 -0
  34. data/test/test_helper.rb +1 -0
  35. data/test/travis/database.yml +4 -0
  36. metadata +22 -13
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YzExZGVjNzkyYjBhNmZiMDE4MmQ4NTBmMTNiMGFlOTg3M2ZjMjcyOA==
5
- data.tar.gz: !binary |-
6
- MDRjY2QxYTc1NmViOGI5MDM1Yjg5ODY1YmY3ZjU0ZGJkMGRiOTFhZA==
2
+ SHA1:
3
+ metadata.gz: 8a9d274345964888ca0c5b9242a689d3953271c7
4
+ data.tar.gz: 59dcdf278fec2582d2e44a7fb416d355026856a5
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- YmUyZGIyZWQ1MjI1MGZkN2VkZTk0NGZlZmUyZWFmZDY0OTc3NjU3OTQzZWI5
10
- ZTk2NGFkNjc1MjVkOGE2MGQyNTM2OTc1N2YzYjQyYmFlNjA0M2FhNjRiMDEz
11
- YTgyNTk4MjIxOTUwYzYwOWM1ZmE5ZGQ0NDcyODkzNGNkNGYwMWU=
12
- data.tar.gz: !binary |-
13
- ZmRlNTc0NjhhNmYxZmNiNDFjNzZlNTM1ODcxNTNiNzE3NmUyYjhjMjliYjU4
14
- MGY5NmYyN2JhY2FjMmQ3ZWFiODE3ZTY3OTgxMDA5M2Y5MTkwZjIyNWU2Y2Ni
15
- MWUyYTRlODNiMzg1YzYyNDEzMmFjZTBhZTEwMzFkZGU0MDI5ZTE=
6
+ metadata.gz: 96f341704f5a40797d626039985e52813d01087e9d41427ef375b5c383c1ce7f22cfac0120939e52ea037b86d84c8ae491ea098f82c2bcc6a94fea34a823dd02
7
+ data.tar.gz: 4d0f4890f4b97ea9c3d36bf8971d6334ab880d235eda6c14a2af7a8395900562fdb09fd2127c55d3283f48a8734e543671eb2ce0734b1f91689faa95a7bbff56
@@ -19,6 +19,7 @@ matrix:
19
19
  - rvm: jruby-9.0.5.0
20
20
  env: AR_VERSION=4.2
21
21
  script:
22
+ - bundle exec rake test:jdbcsqlite3
22
23
  - bundle exec rake test:jdbcmysql
23
24
  - bundle exec rake test:jdbcpostgresql
24
25
 
@@ -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 vi \#324
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
@@ -17,6 +17,7 @@ end
17
17
  platforms :jruby do
18
18
  gem "jdbc-mysql"
19
19
  gem "jdbc-postgres"
20
+ gem "activerecord-jdbcsqlite3-adapter"
20
21
  gem "activerecord-jdbcmysql-adapter"
21
22
  gem "activerecord-jdbcpostgresql-adapter"
22
23
  end
data/Rakefile CHANGED
@@ -18,6 +18,7 @@ ADAPTERS = %w(
18
18
  mysql2_makara
19
19
  mysql2spatial
20
20
  jdbcmysql
21
+ jdbcsqlite3
21
22
  jdbcpostgresql
22
23
  postgresql
23
24
  postgresql_makara
@@ -1,6 +1,7 @@
1
1
  require 'pathname'
2
2
  require "fileutils"
3
3
  require "active_record"
4
+ require "active_record/base"
4
5
 
5
6
  benchmark_dir = File.dirname(__FILE__)
6
7
 
@@ -1,3 +1,2 @@
1
- platforms :ruby do
2
- gem 'activerecord', '~> 3.2.0'
3
- end
1
+ gem 'activerecord', '~> 3.2.0'
2
+ gem 'composite_primary_keys', '~> 5.0'
@@ -1,3 +1,2 @@
1
- platforms :ruby do
2
- gem 'activerecord', '~> 4.0.0'
3
- end
1
+ gem 'activerecord', '~> 4.0.0'
2
+ gem 'composite_primary_keys', '~> 6.0'
@@ -1,3 +1,2 @@
1
- platforms :ruby do
2
- gem 'activerecord', '~> 4.1.0'
3
- end
1
+ gem 'activerecord', '~> 4.1.0'
2
+ gem 'composite_primary_keys', '~> 7.0'
@@ -1,7 +1,2 @@
1
- platforms :ruby do
2
- gem 'activerecord', '~> 4.2.0'
3
- end
4
-
5
- platforms :jruby do
6
- gem 'activerecord', '~> 4.2.0'
7
- end
1
+ gem 'activerecord', '~> 4.2.0'
2
+ gem 'composite_primary_keys', '~> 8.0'
@@ -1,3 +1,2 @@
1
- platforms :ruby do
2
- gem 'activerecord', '~> 5.0.0'
3
- end
1
+ gem 'activerecord', '~> 5.0.0'
2
+ gem 'composite_primary_keys', '~> 9.0'
@@ -0,0 +1,6 @@
1
+ require "active_record/connection_adapters/sqlite3_adapter"
2
+ require "activerecord-import/adapters/sqlite3_adapter"
3
+
4
+ class ActiveRecord::ConnectionAdapters::SQLite3Adapter
5
+ include ActiveRecord::Import::SQLite3Adapter
6
+ end
@@ -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 < max
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
- sql << "RETURNING #{options[:primary_key]}"
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
- conflict_target = primary_key( table_name )
140
- "(#{conflict_target}) " if conflict_target
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
- last_insert_id = insert( sql2insert, *args )
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, ids]
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
- if !column_names.include?(primary_key) && connection.prefetch_primary_key? && sequence_name
408
- column_names << primary_key
409
- array_of_attributes.each { |a| a << nil }
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 gets called a *lot* in this high intensity loop.
630
- # Reuse the same one w/in the loop, otherwise it would keep being re-retreived (= lots of time for large imports)
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 respond_to?(:type_caster) && type_caster.respond_to?(:type_cast_for_database) # Rails 5.0 and higher
641
- connection_memo.quote(type_caster.type_cast_for_database(column.name, val))
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[:max_bytes]
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
- [*value_sets]
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
 
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Import
3
- VERSION = "0.16.2".freeze
3
+ VERSION = "0.17.0".freeze
4
4
  end
5
5
  end
@@ -0,0 +1 @@
1
+ ENV["ARE_DB"] = "jdbcsqlite3"
@@ -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")]
@@ -1,5 +1,4 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
-
3
2
  require File.expand_path(File.dirname(__FILE__) + '/../support/assertions')
4
3
  require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
4
 
@@ -1,5 +1,4 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
-
3
2
  require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
4
3
 
5
4
  should_support_postgresql_import_functionality
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../support/sqlite3/import_examples')
3
+
4
+ should_support_sqlite3_import_functionality
@@ -1,5 +1,7 @@
1
1
  class Book < ActiveRecord::Base
2
2
  belongs_to :topic, inverse_of: :books
3
+ belongs_to :tag, foreign_key: [:tag_id, :parent_id]
4
+
3
5
  has_many :chapters, inverse_of: :book
4
6
  has_many :discounts, as: :discountable
5
7
  has_many :end_notes, inverse_of: :book
@@ -1,2 +1,4 @@
1
+ require_relative 'book'
2
+
1
3
  class Dictionary < Book
2
4
  end
@@ -0,0 +1,4 @@
1
+ class Tag < ActiveRecord::Base
2
+ self.primary_keys = :tag_id, :publisher_id
3
+ has_many :books, inverse_of: :tag
4
+ end
@@ -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
@@ -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
- should_support_recursive_import
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
@@ -26,6 +26,7 @@ end
26
26
 
27
27
  require 'timecop'
28
28
  require 'chronic'
29
+ require 'composite_primary_keys'
29
30
 
30
31
  require "ruby-debug" if RUBY_VERSION.to_f < 1.9
31
32
 
@@ -15,6 +15,10 @@ jdbcmysql: &mysql2
15
15
  <<: *common
16
16
  adapter: jdbcmysql
17
17
 
18
+ jdbcsqlite3: &sqlite3
19
+ <<: *common
20
+ adapter: jdbcsqlite3
21
+
18
22
  mysql2: &mysql2
19
23
  <<: *common
20
24
  adapter: mysql2
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.16.2
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: 2016-11-17 00:00:00.000000000 Z
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.4.8
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