crashtech/torque-postgresql

deduplicated: undefined method '-@' for []:Array with Rails 6.1 and arrays with default value

Closed this issue · 6 comments

4ndv commented

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

4ndv commented

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.

4ndv commented

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.

4ndv commented

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.