deduplicated: undefined method '-@' for []:Array with Rails 6.1 and arrays with default value
Closed this issue · 6 comments
Here's test code:
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Activate the gem you are reporting the issue against.
gem "activerecord", "6.1.0"
gem "torque-postgresql"
gem "pg"
end
require "active_record"
require "logger"
ActiveRecord::Base.establish_connection(
adapter: "postgresql",
database: "torque_pg_test",
encoding: "unicode",
host: "localhost",
port: "5432",
password: "postgres",
username: "postgres"
)
ActiveRecord::Schema.define do
drop_table "employees" if table_exists?("employees")
drop_table "projects" if table_exists?("projects")
create_table "employees" do |t|
t.string "name"
t.timestamps
end
create_table "projects" do |t|
t.bigint "employees_ids", array: true, default: []
t.string "title"
t.timestamps
end
end
class Employee < ActiveRecord::Base
end
class Project < ActiveRecord::Base
belongs_to_many :employees, foreign_key: :employees_ids
end
Project.inspect
Notice array: true, default: []
in t.bigint "employees_ids", array: true, default: []
When calling Project.inspect
since Rails 6.1 there is an exception in AR:
lib/active_record/connection_adapters/column.rb:94:in `deduplicated': undefined method `-@' for []:Array (NoMethodError)
Looks like AR now tries to unfreeze @default
, which cannot be done to an Array
Also it fails on any array with such default, even without belongs_to_many:
ActiveRecord::Schema.define do
drop_table "employees" if table_exists?("employees")
create_table "employees" do |t|
t.string "name"
t.bigint "some_array", array: true, default: []
t.timestamps
end
end
class Employee < ActiveRecord::Base
end
Employee.inspect
I think that for Rails 6.1, anything more complex than a pure string needs now to be provided as a Proc.
Have you tried the following?
ActiveRecord::Schema.define do
drop_table "employees" if table_exists?("employees")
create_table "employees" do |t|
t.string "name"
t.bigint "some_array", array: true, default: -> { [] }
t.timestamps
end
end
class Employee < ActiveRecord::Base
end
Employee.inspect
I can try to re-allow that, but that would be a feature, more than a bug fix.
Fails like this:
1: from /Users/lynx/.asdf/installs/ruby/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-6.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:47:in `block (2 levels) in execute'
/Users/lynx/.asdf/installs/ruby/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-6.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:47:in `exec': PG::SyntaxError: ERROR: syntax error at or near "[" (ActiveRecord::StatementInvalid)
LINE 1: ... character varying, "some_array" bigint[] DEFAULT [], "creat...
@4ndv, you should be able to use the stringified version of a PG array, as in:
create_table "employees" do |t|
t.string "name"
t.bigint "some_array", array: true, default: "{}"
t.timestamps
end
It works with filled values default: "{1}"
, although I do not recommend it.
The problem is that we already have a lot of []
defaults in our DB and after upgrading rails to 6.1 it stopped working. Should we change all the defaults?
Yes. Unfortunately that was never officially accepted by Rails. Now, to use this new version, the change is required. There's no -@
method on Array, and it would be wrong implementing one.