activerecord-import 1.3.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 +4 -4
- data/.github/workflows/test.yaml +39 -10
- data/.rubocop.yml +74 -8
- data/Brewfile +3 -1
- data/CHANGELOG.md +12 -0
- data/Gemfile +5 -7
- data/README.markdown +9 -8
- data/Rakefile +2 -0
- data/activerecord-import.gemspec +2 -1
- 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/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 +2 -0
- data/gemfiles/6.0.gemfile +2 -0
- data/gemfiles/6.1.gemfile +2 -0
- data/gemfiles/7.0.gemfile +4 -1
- data/lib/activerecord-import/active_record/adapters/abstract_adapter.rb +2 -0
- data/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb +2 -0
- 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 +2 -0
- 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 +3 -1
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +41 -28
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +8 -6
- data/lib/activerecord-import/base.rb +3 -1
- data/lib/activerecord-import/import.rb +21 -10
- 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 +2 -0
- data/lib/activerecord-import/value_sets_parser.rb +2 -0
- data/lib/activerecord-import/version.rb +3 -1
- data/lib/activerecord-import.rb +3 -1
- data/test/adapters/jdbcmysql.rb +2 -0
- data/test/adapters/jdbcpostgresql.rb +2 -0
- data/test/adapters/jdbcsqlite3.rb +2 -0
- data/test/adapters/makara_postgis.rb +2 -0
- data/test/adapters/mysql2.rb +2 -0
- data/test/adapters/mysql2_makara.rb +2 -0
- data/test/adapters/mysql2spatial.rb +2 -0
- data/test/adapters/postgis.rb +2 -0
- data/test/adapters/postgresql.rb +2 -0
- data/test/adapters/postgresql_makara.rb +2 -0
- data/test/adapters/seamless_database_pool.rb +2 -0
- data/test/adapters/spatialite.rb +2 -0
- data/test/adapters/sqlite3.rb +2 -0
- data/test/import_test.rb +26 -1
- 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 +2 -0
- data/test/models/account.rb +2 -0
- data/test/models/alarm.rb +2 -0
- data/test/models/animal.rb +2 -0
- data/test/models/bike_maker.rb +2 -0
- data/test/models/book.rb +2 -0
- data/test/models/car.rb +2 -0
- data/test/models/card.rb +2 -0
- data/test/models/chapter.rb +2 -0
- data/test/models/customer.rb +2 -0
- data/test/models/deck.rb +2 -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 +2 -0
- data/test/models/playing_card.rb +2 -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 +2 -0
- data/test/models/user_token.rb +2 -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 +8 -0
- data/test/schema/jdbcpostgresql_schema.rb +2 -0
- data/test/schema/mysql2_schema.rb +2 -0
- data/test/schema/postgis_schema.rb +2 -0
- data/test/schema/postgresql_schema.rb +2 -0
- data/test/schema/sqlite3_schema.rb +2 -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 +2 -0
- data/test/support/generate.rb +4 -2
- data/test/support/mysql/import_examples.rb +2 -1
- data/test/support/postgresql/import_examples.rb +41 -2
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +2 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +2 -0
- data/test/support/shared_examples/recursive_import.rb +2 -0
- data/test/support/sqlite3/import_examples.rb +2 -1
- data/test/synchronize_test.rb +2 -0
- data/test/test_helper.rb +18 -2
- data/test/value_sets_bytes_parser_test.rb +2 -0
- data/test/value_sets_records_parser_test.rb +2 -0
- metadata +5 -3
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord::Import::PostgreSQLAdapter
|
|
2
4
|
include ActiveRecord::Import::ImportSupport
|
|
3
5
|
include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
|
|
@@ -6,7 +8,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
6
8
|
|
|
7
9
|
def insert_many( sql, values, options = {}, *args ) # :nodoc:
|
|
8
10
|
number_of_inserts = 1
|
|
9
|
-
returned_values =
|
|
11
|
+
returned_values = {}
|
|
10
12
|
ids = []
|
|
11
13
|
results = []
|
|
12
14
|
|
|
@@ -18,47 +20,53 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
18
20
|
|
|
19
21
|
sql2insert = base_sql + values.join( ',' ) + post_sql
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
if
|
|
23
|
+
selections = returning_selections(options)
|
|
24
|
+
if selections.blank? || (options[:no_returning] && !options[:recursive])
|
|
23
25
|
insert( sql2insert, *args )
|
|
24
26
|
else
|
|
25
|
-
returned_values = if
|
|
27
|
+
returned_values = if selections.size > 1
|
|
26
28
|
# Select composite columns
|
|
27
|
-
|
|
29
|
+
db_result = select_all( sql2insert, *args )
|
|
30
|
+
{ values: db_result.rows, columns: db_result.columns }
|
|
28
31
|
else
|
|
29
|
-
select_values( sql2insert, *args )
|
|
32
|
+
{ values: select_values( sql2insert, *args ) }
|
|
30
33
|
end
|
|
31
34
|
clear_query_cache if query_cache_enabled
|
|
32
35
|
end
|
|
33
36
|
|
|
34
37
|
if options[:returning].blank?
|
|
35
|
-
ids = returned_values
|
|
38
|
+
ids = Array(returned_values[:values])
|
|
36
39
|
elsif options[:primary_key].blank?
|
|
37
|
-
|
|
40
|
+
options[:returning_columns] ||= returned_values[:columns]
|
|
41
|
+
results = Array(returned_values[:values])
|
|
38
42
|
else
|
|
39
43
|
# split primary key and returning columns
|
|
40
|
-
ids, results = split_ids_and_results(returned_values,
|
|
44
|
+
ids, results, options[:returning_columns] = split_ids_and_results(returned_values, options)
|
|
41
45
|
end
|
|
42
46
|
|
|
43
47
|
ActiveRecord::Import::Result.new([], number_of_inserts, ids, results)
|
|
44
48
|
end
|
|
45
49
|
|
|
46
|
-
def split_ids_and_results(
|
|
50
|
+
def split_ids_and_results( selections, options )
|
|
47
51
|
ids = []
|
|
48
|
-
|
|
52
|
+
returning_values = []
|
|
53
|
+
|
|
54
|
+
columns = Array(selections[:columns])
|
|
55
|
+
values = Array(selections[:values])
|
|
49
56
|
id_indexes = Array(options[:primary_key]).map { |key| columns.index(key) }
|
|
50
|
-
|
|
57
|
+
returning_columns = columns.reject.with_index { |_, index| id_indexes.include?(index) }
|
|
58
|
+
returning_indexes = returning_columns.map { |column| columns.index(column) }
|
|
51
59
|
|
|
52
60
|
values.each do |value|
|
|
53
61
|
value_array = Array(value)
|
|
54
|
-
ids << id_indexes.map { |
|
|
55
|
-
|
|
62
|
+
ids << id_indexes.map { |index| value_array[index] }
|
|
63
|
+
returning_values << returning_indexes.map { |index| value_array[index] }
|
|
56
64
|
end
|
|
57
65
|
|
|
58
66
|
ids.map!(&:first) if id_indexes.size == 1
|
|
59
|
-
|
|
67
|
+
returning_values.map!(&:first) if returning_columns.size == 1
|
|
60
68
|
|
|
61
|
-
[ids,
|
|
69
|
+
[ids, returning_values, returning_columns]
|
|
62
70
|
end
|
|
63
71
|
|
|
64
72
|
def next_value_for_sequence(sequence_name)
|
|
@@ -79,19 +87,24 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
79
87
|
|
|
80
88
|
sql += super(table_name, options)
|
|
81
89
|
|
|
82
|
-
|
|
83
|
-
unless
|
|
84
|
-
sql << " RETURNING
|
|
90
|
+
selections = returning_selections(options)
|
|
91
|
+
unless selections.blank? || (options[:no_returning] && !options[:recursive])
|
|
92
|
+
sql << " RETURNING #{selections.join(', ')}"
|
|
85
93
|
end
|
|
86
94
|
|
|
87
95
|
sql
|
|
88
96
|
end
|
|
89
97
|
|
|
90
|
-
def
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
def returning_selections(options)
|
|
99
|
+
selections = []
|
|
100
|
+
column_names = Array(options[:model].column_names)
|
|
101
|
+
|
|
102
|
+
selections += Array(options[:primary_key]) if options[:primary_key].present?
|
|
103
|
+
selections += Array(options[:returning]) if options[:returning].present?
|
|
104
|
+
|
|
105
|
+
selections.map do |selection|
|
|
106
|
+
column_names.include?(selection.to_s) ? "\"#{selection}\"" : selection
|
|
107
|
+
end
|
|
95
108
|
end
|
|
96
109
|
|
|
97
110
|
# Add a column to be updated on duplicate key update
|
|
@@ -123,7 +136,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
123
136
|
arg = { columns: arg } if arg.is_a?( Array ) || arg.is_a?( String )
|
|
124
137
|
return unless arg.is_a?( Hash )
|
|
125
138
|
|
|
126
|
-
sql = ' ON CONFLICT '
|
|
139
|
+
sql = ' ON CONFLICT '.dup
|
|
127
140
|
conflict_target = sql_for_conflict_target( arg )
|
|
128
141
|
|
|
129
142
|
columns = arg.fetch( :columns, [] )
|
|
@@ -179,9 +192,9 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
|
179
192
|
if constraint_name.present?
|
|
180
193
|
"ON CONSTRAINT #{constraint_name} "
|
|
181
194
|
elsif conflict_target.present?
|
|
182
|
-
'('
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
sql = '(' + Array( conflict_target ).reject( &:blank? ).join( ', ' ) + ') '
|
|
196
|
+
sql += "WHERE #{index_predicate} " if index_predicate
|
|
197
|
+
sql
|
|
185
198
|
end
|
|
186
199
|
end
|
|
187
200
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord::Import::SQLite3Adapter
|
|
2
4
|
include ActiveRecord::Import::ImportSupport
|
|
3
5
|
include ActiveRecord::Import::OnDuplicateKeyUpdateSupport
|
|
4
6
|
|
|
5
|
-
MIN_VERSION_FOR_IMPORT = "3.7.11"
|
|
6
|
-
MIN_VERSION_FOR_UPSERT = "3.24.0"
|
|
7
|
+
MIN_VERSION_FOR_IMPORT = "3.7.11"
|
|
8
|
+
MIN_VERSION_FOR_UPSERT = "3.24.0"
|
|
7
9
|
SQLITE_LIMIT_COMPOUND_SELECT = 500
|
|
8
10
|
|
|
9
11
|
# Override our conformance to ActiveRecord::Import::ImportSupport interface
|
|
@@ -97,7 +99,7 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
97
99
|
arg = { columns: arg } if arg.is_a?( Array ) || arg.is_a?( String )
|
|
98
100
|
return unless arg.is_a?( Hash )
|
|
99
101
|
|
|
100
|
-
sql = ' ON CONFLICT '
|
|
102
|
+
sql = ' ON CONFLICT '.dup
|
|
101
103
|
conflict_target = sql_for_conflict_target( arg )
|
|
102
104
|
|
|
103
105
|
columns = arg.fetch( :columns, [] )
|
|
@@ -150,9 +152,9 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
|
150
152
|
conflict_target = args[:conflict_target]
|
|
151
153
|
index_predicate = args[:index_predicate]
|
|
152
154
|
if conflict_target.present?
|
|
153
|
-
'('
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
sql = '(' + Array( conflict_target ).reject( &:blank? ).join( ', ' ) + ') '
|
|
156
|
+
sql += "WHERE #{index_predicate} " if index_predicate
|
|
157
|
+
sql
|
|
156
158
|
end
|
|
157
159
|
end
|
|
158
160
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "pathname"
|
|
2
4
|
require "active_record"
|
|
3
5
|
require "active_record/version"
|
|
4
6
|
|
|
5
7
|
module ActiveRecord::Import
|
|
6
|
-
ADAPTER_PATH = "activerecord-import/active_record/adapters"
|
|
8
|
+
ADAPTER_PATH = "activerecord-import/active_record/adapters"
|
|
7
9
|
|
|
8
10
|
def self.base_adapter(adapter)
|
|
9
11
|
case adapter
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "ostruct"
|
|
2
4
|
|
|
3
5
|
module ActiveRecord::Import::ConnectionAdapters; end
|
|
@@ -55,7 +57,7 @@ module ActiveRecord::Import #:nodoc:
|
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
59
|
|
|
58
|
-
filter.instance_variable_set(:@attributes, attrs)
|
|
60
|
+
filter.instance_variable_set(:@attributes, attrs.flatten)
|
|
59
61
|
|
|
60
62
|
if @validate_callbacks.respond_to?(:chain, true)
|
|
61
63
|
@validate_callbacks.send(:chain).tap do |chain|
|
|
@@ -547,7 +549,7 @@ class ActiveRecord::Base
|
|
|
547
549
|
alias import! bulk_import! unless ActiveRecord::Base.respond_to? :import!
|
|
548
550
|
|
|
549
551
|
def import_helper( *args )
|
|
550
|
-
options = { validate: true, timestamps: true, track_validation_failures: false }
|
|
552
|
+
options = { model: self, validate: true, timestamps: true, track_validation_failures: false }
|
|
551
553
|
options.merge!( args.pop ) if args.last.is_a? Hash
|
|
552
554
|
# making sure that current model's primary key is used
|
|
553
555
|
options[:primary_key] = primary_key
|
|
@@ -875,19 +877,28 @@ class ActiveRecord::Base
|
|
|
875
877
|
end
|
|
876
878
|
end
|
|
877
879
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
880
|
+
set_value = lambda do |model, column, value|
|
|
881
|
+
val = deserialize_value.call(column, value)
|
|
882
|
+
if model.attribute_names.include?(column)
|
|
883
|
+
model.send("#{column}=", val)
|
|
884
|
+
else
|
|
885
|
+
attributes = attributes_builder.build_from_database(model.attributes.merge(column => val))
|
|
886
|
+
model.instance_variable_set(:@attributes, attributes)
|
|
887
|
+
end
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
columns = Array(options[:returning_columns])
|
|
891
|
+
results = Array(import_result.results)
|
|
892
|
+
if models.size == results.size
|
|
893
|
+
single_column = columns.first if columns.size == 1
|
|
894
|
+
results.each_with_index do |result, index|
|
|
882
895
|
model = models[index]
|
|
883
896
|
|
|
884
897
|
if single_column
|
|
885
|
-
|
|
886
|
-
model.send(single_column, val)
|
|
898
|
+
set_value.call(model, single_column, result)
|
|
887
899
|
else
|
|
888
900
|
columns.each_with_index do |column, col_index|
|
|
889
|
-
|
|
890
|
-
model.send("#{column}=", val)
|
|
901
|
+
set_value.call(model, column, result[col_index])
|
|
891
902
|
end
|
|
892
903
|
end
|
|
893
904
|
end
|
data/lib/activerecord-import.rb
CHANGED
data/test/adapters/jdbcmysql.rb
CHANGED
data/test/adapters/mysql2.rb
CHANGED
data/test/adapters/postgis.rb
CHANGED
data/test/adapters/postgresql.rb
CHANGED
data/test/adapters/spatialite.rb
CHANGED
data/test/adapters/sqlite3.rb
CHANGED
data/test/import_test.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require File.expand_path('../test_helper', __FILE__)
|
|
2
4
|
|
|
3
5
|
describe "#import" do
|
|
@@ -159,6 +161,25 @@ describe "#import" do
|
|
|
159
161
|
Tag.import columns, values, validate: false
|
|
160
162
|
end
|
|
161
163
|
end
|
|
164
|
+
|
|
165
|
+
it "should import models that are required to belong to models with composite primary keys" do
|
|
166
|
+
tag = Tag.create!(tag_id: 1, publisher_id: 1, tag: 'Mystery')
|
|
167
|
+
valid_tag_alias = TagAlias.new(tag_id: tag.tag_id, parent_id: tag.publisher_id, alias: 'Detective')
|
|
168
|
+
invalid_tag_aliases = [
|
|
169
|
+
TagAlias.new(tag_id: nil, parent_id: nil, alias: 'Detective'),
|
|
170
|
+
TagAlias.new(tag_id: tag.tag_id, parent_id: nil, alias: 'Detective'),
|
|
171
|
+
TagAlias.new(tag_id: nil, parent_id: tag.publisher_id, alias: 'Detective'),
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
assert_difference "TagAlias.count", +1 do
|
|
175
|
+
TagAlias.import [valid_tag_alias]
|
|
176
|
+
end
|
|
177
|
+
invalid_tag_aliases.each do |invalid_tag_alias|
|
|
178
|
+
assert_no_difference "TagAlias.count" do
|
|
179
|
+
TagAlias.import [invalid_tag_alias]
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
162
183
|
end
|
|
163
184
|
end
|
|
164
185
|
|
|
@@ -555,7 +576,11 @@ describe "#import" do
|
|
|
555
576
|
context "when the timestamps columns are present" do
|
|
556
577
|
setup do
|
|
557
578
|
@existing_book = Book.create(title: "Fell", author_name: "Curry", publisher: "Bayer", created_at: 2.years.ago.utc, created_on: 2.years.ago.utc, updated_at: 2.years.ago.utc, updated_on: 2.years.ago.utc)
|
|
558
|
-
ActiveRecord
|
|
579
|
+
if ActiveRecord.respond_to?(:default_timezone)
|
|
580
|
+
ActiveRecord.default_timezone = :utc
|
|
581
|
+
else
|
|
582
|
+
ActiveRecord::Base.default_timezone = :utc
|
|
583
|
+
end
|
|
559
584
|
Timecop.freeze(time) do
|
|
560
585
|
assert_difference "Book.count", +2 do
|
|
561
586
|
Book.import %w(title author_name publisher created_at created_on updated_at updated_on), [["LDAP", "Big Bird", "Del Rey", nil, nil, nil, nil], [@existing_book.title, @existing_book.author_name, @existing_book.publisher, @existing_book.created_at, @existing_book.created_on, @existing_book.updated_at, @existing_book.updated_on]]
|
data/test/models/account.rb
CHANGED
data/test/models/alarm.rb
CHANGED
data/test/models/animal.rb
CHANGED
data/test/models/bike_maker.rb
CHANGED
data/test/models/book.rb
CHANGED
data/test/models/car.rb
CHANGED
data/test/models/card.rb
CHANGED
data/test/models/chapter.rb
CHANGED
data/test/models/customer.rb
CHANGED
data/test/models/deck.rb
CHANGED
data/test/models/dictionary.rb
CHANGED
data/test/models/discount.rb
CHANGED
data/test/models/end_note.rb
CHANGED
data/test/models/group.rb
CHANGED
data/test/models/order.rb
CHANGED
data/test/models/playing_card.rb
CHANGED
data/test/models/promotion.rb
CHANGED
data/test/models/question.rb
CHANGED
data/test/models/rule.rb
CHANGED
data/test/models/tag.rb
CHANGED
data/test/models/topic.rb
CHANGED
data/test/models/user.rb
CHANGED
data/test/models/user_token.rb
CHANGED
data/test/models/vendor.rb
CHANGED
data/test/models/widget.rb
CHANGED
data/test/mysql2/import_test.rb
CHANGED