Skip to content

Incorrect default value inserted for JSON column types #82

@fschwahn

Description

@fschwahn

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions