activerecord-import 0.10.0 → 1.0.8
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/.gitignore +1 -0
- data/.rubocop.yml +49 -0
- data/.rubocop_todo.yml +36 -0
- data/.travis.yml +64 -8
- data/CHANGELOG.md +475 -0
- data/Gemfile +32 -15
- data/LICENSE +21 -56
- data/README.markdown +564 -35
- data/Rakefile +20 -3
- data/activerecord-import.gemspec +7 -7
- data/benchmarks/README +2 -2
- data/benchmarks/benchmark.rb +68 -64
- data/benchmarks/lib/base.rb +138 -137
- data/benchmarks/lib/cli_parser.rb +107 -103
- data/benchmarks/lib/{mysql_benchmark.rb → mysql2_benchmark.rb} +19 -22
- data/benchmarks/lib/output_to_csv.rb +5 -4
- data/benchmarks/lib/output_to_html.rb +8 -13
- data/benchmarks/models/test_innodb.rb +1 -1
- data/benchmarks/models/test_memory.rb +1 -1
- data/benchmarks/models/test_myisam.rb +1 -1
- data/benchmarks/schema/mysql2_schema.rb +16 -0
- data/gemfiles/3.2.gemfile +2 -4
- data/gemfiles/4.0.gemfile +2 -4
- data/gemfiles/4.1.gemfile +2 -4
- data/gemfiles/4.2.gemfile +2 -4
- data/gemfiles/5.0.gemfile +2 -0
- data/gemfiles/5.1.gemfile +2 -0
- data/gemfiles/5.2.gemfile +2 -0
- data/gemfiles/6.0.gemfile +2 -0
- data/gemfiles/6.1.gemfile +1 -0
- data/lib/activerecord-import/active_record/adapters/jdbcsqlite3_adapter.rb +6 -0
- data/lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb +0 -1
- data/lib/activerecord-import/adapters/abstract_adapter.rb +23 -17
- data/lib/activerecord-import/adapters/mysql_adapter.rb +52 -25
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +187 -10
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +148 -17
- data/lib/activerecord-import/base.rb +15 -9
- data/lib/activerecord-import/import.rb +740 -191
- data/lib/activerecord-import/synchronize.rb +21 -21
- data/lib/activerecord-import/value_sets_parser.rb +33 -8
- data/lib/activerecord-import/version.rb +1 -1
- data/lib/activerecord-import.rb +4 -15
- data/test/adapters/jdbcsqlite3.rb +1 -0
- data/test/adapters/makara_postgis.rb +1 -0
- data/test/adapters/mysql2_makara.rb +1 -0
- data/test/adapters/mysql2spatial.rb +1 -1
- data/test/adapters/postgis.rb +1 -1
- data/test/adapters/postgresql.rb +1 -1
- data/test/adapters/postgresql_makara.rb +1 -0
- data/test/adapters/spatialite.rb +1 -1
- data/test/adapters/sqlite3.rb +1 -1
- data/test/database.yml.sample +13 -18
- data/test/import_test.rb +608 -89
- data/test/jdbcmysql/import_test.rb +2 -3
- data/test/jdbcpostgresql/import_test.rb +0 -2
- data/test/jdbcsqlite3/import_test.rb +4 -0
- data/test/makara_postgis/import_test.rb +8 -0
- data/test/models/account.rb +3 -0
- data/test/models/alarm.rb +2 -0
- data/test/models/animal.rb +6 -0
- data/test/models/bike_maker.rb +7 -0
- data/test/models/book.rb +7 -6
- data/test/models/car.rb +3 -0
- data/test/models/chapter.rb +2 -2
- data/test/models/dictionary.rb +4 -0
- data/test/models/discount.rb +3 -0
- data/test/models/end_note.rb +2 -2
- data/test/models/promotion.rb +3 -0
- data/test/models/question.rb +3 -0
- data/test/models/rule.rb +3 -0
- data/test/models/tag.rb +4 -0
- data/test/models/topic.rb +17 -3
- data/test/models/user.rb +3 -0
- data/test/models/user_token.rb +4 -0
- data/test/models/vendor.rb +7 -0
- data/test/models/widget.rb +19 -2
- data/test/mysql2/import_test.rb +2 -3
- data/test/{em_mysql2 → mysql2_makara}/import_test.rb +1 -1
- data/test/mysqlspatial2/import_test.rb +2 -2
- data/test/postgis/import_test.rb +5 -1
- data/test/schema/generic_schema.rb +159 -85
- data/test/schema/jdbcpostgresql_schema.rb +1 -0
- data/test/schema/mysql2_schema.rb +19 -0
- data/test/schema/postgis_schema.rb +1 -0
- data/test/schema/postgresql_schema.rb +61 -0
- data/test/schema/sqlite3_schema.rb +13 -0
- data/test/sqlite3/import_test.rb +2 -50
- data/test/support/active_support/test_case_extensions.rb +21 -13
- data/test/support/{mysql/assertions.rb → assertions.rb} +20 -2
- data/test/support/factories.rb +39 -14
- data/test/support/generate.rb +10 -10
- data/test/support/mysql/import_examples.rb +49 -98
- data/test/support/postgresql/import_examples.rb +535 -57
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +43 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +378 -0
- data/test/support/shared_examples/recursive_import.rb +225 -0
- data/test/support/sqlite3/import_examples.rb +231 -0
- data/test/synchronize_test.rb +10 -2
- data/test/test_helper.rb +36 -8
- data/test/travis/database.yml +26 -17
- data/test/value_sets_bytes_parser_test.rb +25 -17
- data/test/value_sets_records_parser_test.rb +6 -6
- metadata +86 -42
- data/benchmarks/boot.rb +0 -18
- data/benchmarks/schema/mysql_schema.rb +0 -16
- data/gemfiles/3.1.gemfile +0 -4
- data/lib/activerecord-import/active_record/adapters/em_mysql2_adapter.rb +0 -8
- data/lib/activerecord-import/active_record/adapters/mysql_adapter.rb +0 -6
- data/lib/activerecord-import/em_mysql2.rb +0 -7
- data/lib/activerecord-import/mysql.rb +0 -7
- data/test/adapters/em_mysql2.rb +0 -1
- data/test/adapters/mysql.rb +0 -1
- data/test/adapters/mysqlspatial.rb +0 -1
- data/test/mysql/import_test.rb +0 -6
- data/test/mysqlspatial/import_test.rb +0 -6
- data/test/schema/mysql_schema.rb +0 -18
- data/test/travis/build.sh +0 -30
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
module ActiveRecord # :nodoc:
|
|
2
2
|
class Base # :nodoc:
|
|
3
|
-
|
|
4
3
|
# Synchronizes the passed in ActiveRecord instances with data
|
|
5
4
|
# from the database. This is like calling reload on an individual
|
|
6
5
|
# ActiveRecord instance but it is intended for use on multiple instances.
|
|
@@ -21,45 +20,46 @@ module ActiveRecord # :nodoc:
|
|
|
21
20
|
# Post.synchronize posts, [:name] # queries on the :name column and not the :id column
|
|
22
21
|
# posts.first.address # => "1245 Foo Ln" instead of whatever it was
|
|
23
22
|
#
|
|
24
|
-
def self.synchronize(instances, keys=[
|
|
23
|
+
def self.synchronize(instances, keys = [primary_key])
|
|
25
24
|
return if instances.empty?
|
|
26
25
|
|
|
27
26
|
conditions = {}
|
|
28
|
-
order = ""
|
|
29
27
|
|
|
30
|
-
key_values = keys.map { |key| instances.map(&
|
|
28
|
+
key_values = keys.map { |key| instances.map(&key.to_sym) }
|
|
31
29
|
keys.zip(key_values).each { |key, values| conditions[key] = values }
|
|
32
|
-
order = keys.map{ |key| "#{key} ASC" }.join(",")
|
|
30
|
+
order = keys.map { |key| "#{key} ASC" }.join(",")
|
|
33
31
|
|
|
34
32
|
klass = instances.first.class
|
|
35
33
|
|
|
36
|
-
fresh_instances = klass.where(conditions).order(order)
|
|
34
|
+
fresh_instances = klass.unscoped.where(conditions).order(order)
|
|
37
35
|
instances.each do |instance|
|
|
38
36
|
matched_instance = fresh_instances.detect do |fresh_instance|
|
|
39
|
-
keys.all?{ |key| fresh_instance.send(key) == instance.send(key) }
|
|
37
|
+
keys.all? { |key| fresh_instance.send(key) == instance.send(key) }
|
|
40
38
|
end
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
instance.clear_aggregation_cache
|
|
44
|
-
instance.clear_association_cache
|
|
45
|
-
instance.instance_variable_set :@attributes, matched_instance.instance_variable_get(:@attributes)
|
|
40
|
+
next unless matched_instance
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
instance.changed_attributes.clear # Rails 3.1, 3.2
|
|
51
|
-
end
|
|
42
|
+
instance.send :clear_association_cache
|
|
43
|
+
instance.send :clear_aggregation_cache if instance.respond_to?(:clear_aggregation_cache, true)
|
|
44
|
+
instance.instance_variable_set :@attributes, matched_instance.instance_variable_get(:@attributes)
|
|
52
45
|
|
|
53
|
-
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
instance.instance_variable_set
|
|
46
|
+
if instance.respond_to?(:clear_changes_information)
|
|
47
|
+
instance.clear_changes_information # Rails 4.2 and higher
|
|
48
|
+
else
|
|
49
|
+
instance.instance_variable_set :@attributes_cache, {} # Rails 4.0, 4.1
|
|
50
|
+
instance.changed_attributes.clear # Rails 3.2
|
|
51
|
+
instance.previous_changes.clear
|
|
57
52
|
end
|
|
53
|
+
|
|
54
|
+
# Since the instance now accurately reflects the record in
|
|
55
|
+
# the database, ensure that instance.persisted? is true.
|
|
56
|
+
instance.instance_variable_set '@new_record', false
|
|
57
|
+
instance.instance_variable_set '@destroyed', false
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
# See ActiveRecord::ConnectionAdapters::AbstractAdapter.synchronize
|
|
62
|
-
def synchronize(instances, key=[ActiveRecord::Base.primary_key])
|
|
62
|
+
def synchronize(instances, key = [ActiveRecord::Base.primary_key])
|
|
63
63
|
self.class.synchronize(instances, key)
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
require 'active_support/core_ext/array'
|
|
2
|
+
|
|
1
3
|
module ActiveRecord::Import
|
|
4
|
+
class ValueSetTooLargeError < StandardError
|
|
5
|
+
attr_reader :size
|
|
6
|
+
def initialize(msg = "Value set exceeds max size", size = 0)
|
|
7
|
+
@size = size
|
|
8
|
+
super(msg)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
2
12
|
class ValueSetsBytesParser
|
|
3
13
|
attr_reader :reserved_bytes, :max_bytes, :values
|
|
4
14
|
|
|
@@ -8,30 +18,45 @@ module ActiveRecord::Import
|
|
|
8
18
|
|
|
9
19
|
def initialize(values, options)
|
|
10
20
|
@values = values
|
|
11
|
-
@reserved_bytes = options[:reserved_bytes]
|
|
12
|
-
@max_bytes = options
|
|
21
|
+
@reserved_bytes = options[:reserved_bytes] || 0
|
|
22
|
+
@max_bytes = options.fetch(:max_bytes) { default_max_bytes }
|
|
13
23
|
end
|
|
14
24
|
|
|
15
25
|
def parse
|
|
16
26
|
value_sets = []
|
|
17
|
-
arr
|
|
18
|
-
|
|
27
|
+
arr = []
|
|
28
|
+
current_size = 0
|
|
29
|
+
values.each_with_index do |val, i|
|
|
19
30
|
comma_bytes = arr.size
|
|
31
|
+
insert_size = reserved_bytes + val.bytesize
|
|
32
|
+
|
|
33
|
+
if insert_size > max_bytes
|
|
34
|
+
raise ValueSetTooLargeError.new("#{insert_size} bytes exceeds the max allowed for an insert [#{@max_bytes}]", insert_size)
|
|
35
|
+
end
|
|
36
|
+
|
|
20
37
|
bytes_thus_far = reserved_bytes + current_size + val.bytesize + comma_bytes
|
|
21
38
|
if bytes_thus_far <= max_bytes
|
|
22
39
|
current_size += val.bytesize
|
|
23
40
|
arr << val
|
|
24
41
|
else
|
|
25
42
|
value_sets << arr
|
|
26
|
-
arr = [
|
|
43
|
+
arr = [val]
|
|
27
44
|
current_size = val.bytesize
|
|
28
45
|
end
|
|
29
46
|
|
|
30
47
|
# if we're on the last iteration push whatever we have in arr to value_sets
|
|
31
|
-
value_sets << arr if i == (values.size-1)
|
|
48
|
+
value_sets << arr if i == (values.size - 1)
|
|
32
49
|
end
|
|
33
50
|
|
|
34
|
-
|
|
51
|
+
value_sets
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def default_max_bytes
|
|
57
|
+
values_in_bytes = values.sum(&:bytesize)
|
|
58
|
+
comma_separated_bytes = values.size - 1
|
|
59
|
+
reserved_bytes + values_in_bytes + comma_separated_bytes
|
|
35
60
|
end
|
|
36
61
|
end
|
|
37
62
|
|
|
@@ -48,7 +73,7 @@ module ActiveRecord::Import
|
|
|
48
73
|
end
|
|
49
74
|
|
|
50
75
|
def parse
|
|
51
|
-
@values.in_groups_of(max_records,
|
|
76
|
+
@values.in_groups_of(max_records, false)
|
|
52
77
|
end
|
|
53
78
|
end
|
|
54
79
|
end
|
data/lib/activerecord-import.rb
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
def establish_connection_with_activerecord_import(*args)
|
|
4
|
-
establish_connection_without_activerecord_import(*args)
|
|
5
|
-
ActiveSupport.run_load_hooks(:active_record_connection_established, connection_pool)
|
|
6
|
-
end
|
|
7
|
-
alias_method_chain :establish_connection, :activerecord_import
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
ActiveSupport.on_load(:active_record_connection_established) do |connection_pool|
|
|
12
|
-
if !ActiveRecord.const_defined?(:Import, false) || !ActiveRecord::Import.respond_to?(:load_from_connection_pool)
|
|
13
|
-
require "activerecord-import/base"
|
|
14
|
-
end
|
|
1
|
+
# rubocop:disable Style/FileName
|
|
2
|
+
require "active_support/lazy_load_hooks"
|
|
15
3
|
|
|
16
|
-
|
|
4
|
+
ActiveSupport.on_load(:active_record) do
|
|
5
|
+
require "activerecord-import/base"
|
|
17
6
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ENV["ARE_DB"] = "jdbcsqlite3"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ENV["ARE_DB"] = "postgis"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ENV["ARE_DB"] = "mysql2_makara"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
ENV["ARE_DB"] = "mysql2spatial"
|
|
1
|
+
ENV["ARE_DB"] = "mysql2spatial"
|
data/test/adapters/postgis.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ENV["ARE_DB"] = "postgis"
|
|
1
|
+
ENV["ARE_DB"] = "postgis"
|
data/test/adapters/postgresql.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ENV["ARE_DB"] = "postgresql"
|
|
1
|
+
ENV["ARE_DB"] = "postgresql"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ENV["ARE_DB"] = "postgresql"
|
data/test/adapters/spatialite.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ENV["ARE_DB"] = "spatialite"
|
|
1
|
+
ENV["ARE_DB"] = "spatialite"
|
data/test/adapters/sqlite3.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ENV["ARE_DB"] = "sqlite3"
|
|
1
|
+
ENV["ARE_DB"] = "sqlite3"
|
data/test/database.yml.sample
CHANGED
|
@@ -5,31 +5,15 @@ common: &common
|
|
|
5
5
|
host: localhost
|
|
6
6
|
database: activerecord_import_test
|
|
7
7
|
|
|
8
|
-
mysql: &mysql
|
|
9
|
-
<<: *common
|
|
10
|
-
adapter: mysql
|
|
11
|
-
|
|
12
8
|
mysql2: &mysql2
|
|
13
9
|
<<: *common
|
|
14
10
|
adapter: mysql2
|
|
15
11
|
|
|
16
|
-
mysqlspatial:
|
|
17
|
-
<<: *mysql
|
|
18
|
-
|
|
19
12
|
mysql2spatial:
|
|
20
13
|
<<: *mysql2
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
<<: *
|
|
24
|
-
adapter: em_mysql2
|
|
25
|
-
pool: 5
|
|
26
|
-
|
|
27
|
-
seamless_database_pool:
|
|
28
|
-
<<: *common
|
|
29
|
-
adapter: seamless_database_pool
|
|
30
|
-
pool_adapter: mysql2
|
|
31
|
-
master:
|
|
32
|
-
host: localhost
|
|
15
|
+
mysql2_makara:
|
|
16
|
+
<<: *mysql2
|
|
33
17
|
|
|
34
18
|
postgresql: &postgresql
|
|
35
19
|
<<: *common
|
|
@@ -37,6 +21,9 @@ postgresql: &postgresql
|
|
|
37
21
|
adapter: postgresql
|
|
38
22
|
min_messages: warning
|
|
39
23
|
|
|
24
|
+
postresql_makara:
|
|
25
|
+
<<: *postgresql
|
|
26
|
+
|
|
40
27
|
postgis:
|
|
41
28
|
<<: *postgresql
|
|
42
29
|
|
|
@@ -45,6 +32,14 @@ oracle:
|
|
|
45
32
|
adapter: oracle
|
|
46
33
|
min_messages: debug
|
|
47
34
|
|
|
35
|
+
seamless_database_pool:
|
|
36
|
+
<<: *common
|
|
37
|
+
adapter: seamless_database_pool
|
|
38
|
+
prepared_statements: false
|
|
39
|
+
pool_adapter: mysql2
|
|
40
|
+
master:
|
|
41
|
+
host: localhost
|
|
42
|
+
|
|
48
43
|
sqlite:
|
|
49
44
|
adapter: sqlite
|
|
50
45
|
dbfile: test.db
|