In case of a JSON column, bulk_insert will incorrectly insert the default value. It inserts "{}" (ie. a string) instead of {} (ie. an object).
Here's a failing test:
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem "rails"
gem "sqlite3"
gem "bulk_insert"
end
require "active_record"
require "minitest/autorun"
require "logger"
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :profiles, force: true do |t|
t.string :name
t.json :json_data, default: {}, null: false
end
end
class Profile < ActiveRecord::Base
end
class BugTest < Minitest::Test
def test_bulk_insert_json_default
worker = Profile.bulk_insert
worker.add(name: "Foo", json_data: {})
worker.add(name: "Bar")
worker.save!
profile1 = Profile.find_by!(name: "Foo")
profile2 = Profile.find_by!(name: "Bar")
assert_equal profile1.json_data, {}
assert_equal profile2.json_data, {}
end
end
The reason is the following code:
|
mapped = @columns.map.with_index do |column, index| |
|
value_exists = values.is_a?(Hash) ? values.key?(column.name) : (index < values.length) |
|
if !value_exists |
|
if column.default.present? |
|
column.default |
|
elsif column.name == "created_at" || column.name == "updated_at" |
|
:__timestamp_placeholder |
|
else |
|
nil |
|
end |
|
else |
|
values.is_a?(Hash) ? values[column.name] : values[index] |
|
end |
|
end |
In case a value does not exist, the column default is used. The column default in rails is expressed as a string (ie. in the example above: Profile.columns_hash["json_data"].default == {}). This is problematic for JSON columns, as both "{}" and {} are valid JSON (one a string, the other an object).
In case of a JSON column,
bulk_insertwill incorrectly insert the default value. It inserts"{}"(ie. a string) instead of{}(ie. an object).Here's a failing test:
The reason is the following code:
bulk_insert/lib/bulk_insert/worker.rb
Lines 51 to 64 in ab5db08
In case a value does not exist, the column default is used. The column default in rails is expressed as a string (ie. in the example above:
Profile.columns_hash["json_data"].default == {}). This is problematic for JSON columns, as both"{}"and{}are valid JSON (one a string, the other an object).