rails: ActiveRecord::StatementInvalid error for Rails 6.1 HABTM relationship with particular model name (but fine in Rails 6.0)
I recently updated my app from Rails 6.0 to Rails 6.1 and ran into an error with a has_and_belongs_to_many relationship for a model called “EnabledDynamicField”. I can reproduce this issue in an isolated context in a new Rails 6.1 app.
In the reproduction case below, I’m including other models called “Apple” and “Zebra” to demonstrate that alphabetical sorting in derived join table names doesn’t seem to be related, and to show that a has_and_belongs_to_many relationship between the Apple and Zebra models, by comparison, doesn’t produce any errors.
Steps to reproduce
Create a new Rails 6.1 app (tested with Rails 6.1.4.1) and add following files:
# db/migrate/20210820050644_create_tables.rb
class CreateTables < ActiveRecord::Migration[6.0]
def change
create_table :apples do |t|
t.timestamps
end
create_table :zebras do |t|
t.timestamps
end
create_join_table :apples, :zebras do |t|
t.index :apple_id, name: 'apple_id_app_zeb_join'
t.index :zebra_id, name: 'zebra_id_app_zeb_join'
end
create_table :enabled_dynamic_fields do |t|
t.timestamps
end
create_join_table :apples, :enabled_dynamic_fields do |t|
t.index :apple_id, name: 'apple_id_app_edf_join'
t.index :enabled_dynamic_field_id, name: 'enabled_dynamic_field_id_app_edf_join'
end
create_join_table :enabled_dynamic_fields, :zebras do |t|
t.index :enabled_dynamic_field_id, name: 'enabled_dynamic_field_id_edf_zeb_join'
t.index :zebra_id, name: 'zebra_id_edf_zeb_join'
end
end
end
# app/models/apple.rb
class Apple < ApplicationRecord
has_and_belongs_to_many :enabled_dynamic_fields
has_and_belongs_to_many :zebras
end
# app/models/zebra.rb
class Zebra < ApplicationRecord
has_and_belongs_to_many :apples
has_and_belongs_to_many :enabled_dynamic_fields
end
# app/models/enabled_dynamic_field.rb
class EnabledDynamicField < ApplicationRecord
has_and_belongs_to_many :apples
has_and_belongs_to_many :zebras
end
# test/models/habtm_join_test.rb
require "test_helper"
class HabtmJoinTest < ActiveSupport::TestCase
test "Apple has_and_belongs_to_many Zebras relationship exists" do
apple = Apple.create!
assert apple.zebras.length == 0
end
test "Zebra has_and_belongs_to_many Apples relationship exists" do
zebra = Zebra.create!
assert zebra.apples.length == 0
end
test "Apple has_and_belongs_to_many EnabledDynamicFields relationship exists" do
apple = Apple.create!
assert apple.enabled_dynamic_fields.length == 0
end
test "EnabledDynamicField has_and_belongs_to_many Apples relationship exists" do
enabled_dynamic_field = EnabledDynamicField.create!
assert enabled_dynamic_field.apples.length == 0
end
test "Zebra has_and_belongs_to_many EnabledDynamicFields relationship exists" do
zebra = Zebra.create!
assert zebra.enabled_dynamic_fields.length == 0
end
test "EnabledDynamicField has_and_belongs_to_many Zebras relationship exists" do
enabled_dynamic_field = EnabledDynamicField.create!
assert enabled_dynamic_field.zebras.length == 0
end
end
Run tests:
RAILS_ENV=test ./bin/rails db:migrate
./bin/rails test
If you create the above files and run the tests in a Rails 6.0 app, they all pass. If you do the same thing in Rails 6.1, a couple of tests fail – and to make matters worse it’s not always the same tests (and not always the same error messages). Here are some sample runs:
% ./bin/rails test
Running via Spring preloader in process 42365
Run options: --seed 63981
# Running:
..E
Error:
HabtmJoinTest#test_Apple_has_and_belongs_to_many_EnabledDynamicFields_relationship_exists:
ArgumentError: invalid byte sequence in UTF-8
test/models/habtm_join_test.rb:16:in `block in <class:HabtmJoinTest>'
rails test test/models/habtm_join_test.rb:14
.E
Error:
HabtmJoinTest#test_Zebra_has_and_belongs_to_many_EnabledDynamicFields_relationship_exists:
ActiveRecord::StatementInvalid: SQLite3::SQLException: unrecognized token: """
test/models/habtm_join_test.rb:26:in `block in <class:HabtmJoinTest>'
rails test test/models/habtm_join_test.rb:24
.
Finished in 0.304887s, 19.6794 runs/s, 13.1196 assertions/s.
6 runs, 4 assertions, 0 failures, 2 errors, 0 skips
And here’s another run where the same tests are failing, but the error message is different for one of them (changed from invalid byte sequence
to unrecognized token
:
% ./bin/rails test
Running via Spring preloader in process 42569
Run options: --seed 42091
# Running:
..E
Error:
HabtmJoinTest#test_Zebra_has_and_belongs_to_many_EnabledDynamicFields_relationship_exists:
ActiveRecord::StatementInvalid: SQLite3::SQLException: unrecognized token: "'enabled_dynamic_field_id"
test/models/habtm_join_test.rb:26:in `block in <class:HabtmJoinTest>'
rails test test/models/habtm_join_test.rb:24
..E
Error:
HabtmJoinTest#test_Apple_has_and_belongs_to_many_EnabledDynamicFields_relationship_exists:
ActiveRecord::StatementInvalid: SQLite3::SQLException: unrecognized token: "'enabled_dynamic_field_id"
test/models/habtm_join_test.rb:16:in `block in <class:HabtmJoinTest>'
rails test test/models/habtm_join_test.rb:14
Finished in 0.203738s, 29.4496 runs/s, 19.6331 assertions/s.
6 runs, 4 assertions, 0 failures, 2 errors, 0 skips
And here’s another run where only one test failed:
% ./bin/rails test
Running via Spring preloader in process 42484
Run options: --seed 5607
# Running:
...E
Error:
HabtmJoinTest#test_Apple_has_and_belongs_to_many_EnabledDynamicFields_relationship_exists:
ActiveRecord::StatementInvalid: SQLite3::SQLException: unrecognized token: """
test/models/habtm_join_test.rb:16:in `block in <class:HabtmJoinTest>'
rails test test/models/habtm_join_test.rb:14
..
Finished in 0.253679s, 23.6519 runs/s, 19.7099 assertions/s.
6 runs, 5 assertions, 0 failures, 1 errors, 0 skips
And yet another run where there was only one failure:
% ./bin/rails test
Running via Spring preloader in process 42543
Run options: --seed 17029
# Running:
...E
Error:
HabtmJoinTest#test_Zebra_has_and_belongs_to_many_EnabledDynamicFields_relationship_exists:
ActiveRecord::StatementInvalid: SQLite3::SQLException: unrecognized token: """
test/models/habtm_join_test.rb:26:in `block in <class:HabtmJoinTest>'
rails test test/models/habtm_join_test.rb:24
..
Finished in 0.205232s, 29.2352 runs/s, 24.3627 assertions/s.
6 runs, 5 assertions, 0 failures, 1 errors, 0 skips
Here’s some additional info output from the rails console (in Rails 6.1):
2.6.4 :001 > apple = Apple.create!
(0.5ms) SELECT sqlite_version(*)
TRANSACTION (0.1ms) begin transaction
Apple Create (0.7ms) INSERT INTO "apples" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2021-08-20 06:38:03.350455"], ["updated_at", "2021-08-20 06:38:03.350455"]]
TRANSACTION (2.8ms) commit transaction
=> #<Apple id: 2, created_at: "2021-08-20 06:38:03.350455000 +0000", updated_at: "2021-08-20 06:38:03.350455000 +0000">
2.6.4 :002 > apple.enabled_dynamic_fields.length == 0
Traceback (most recent call last):
1: from (irb):2
ActiveRecord::StatementInvalid (SQLite3::SQLException: unrecognized token: "'")
And:
2.6.4 :001 > zebra = Zebra.create!
(0.5ms) SELECT sqlite_version(*)
TRANSACTION (0.1ms) begin transaction
Zebra Create (0.5ms) INSERT INTO "zebras" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2021-08-20 06:39:02.020261"], ["updated_at", "2021-08-20 06:39:02.020261"]]
TRANSACTION (0.8ms) commit transaction
=> #<Zebra id: 5, created_at: "2021-08-20 06:39:02.020261000 +0000", updated_at: "2021-08-20 06:39:02.020261000 +0000">
2.6.4 :002 > zebra.enabled_dynamic_fields.length == 0
Traceback (most recent call last):
1: from (irb):2
ArgumentError (invalid byte sequence in UTF-8)
So there’s something that Rails doesn’t like about the EnabledDynamicField model.
Expected behavior
Since the above steps worked in Rails 6.0, I also expected them to work in Rails 6.1.
Actual behavior
Fine in Rails 6.0, errors in Rails 6.1.
System configuration
Rails version: 6.1.4.1 Ruby version: 2.6.4p104 Database adapter: sqlite3
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 15 (4 by maintainers)
I wasn’t able to reproduce the error with your file, sorry. I’ve only been able to reproduce it in the context of a full Rails app.