Closed llhhaa closed 3 years ago
The only apparent solution then would be to patch
build_fixture_sql
to dynamically change the engine ofArel::Table
depending on which table it's building for...
I actually like this option depending on a few conditions.
Module.prepend
to be able to super
up to the orig implementation.super
. If those were true, then the patch would be minimally invasive. Otherwise agreed, hacking the internals of build_fixture_sql
seems very brittle if we had to maintain that across Rails versions >= 5.2.
we'll move forward with was the "temporary" adapter patch
I like the idea of the adapter patch if it were only applied for the dev/test environments. Seems it should not run in staging/prod?
After looking at it with @drinks, we ended up with a single-line patch to build_fixture_sql
. This line is the only the changed. Below is the patch:
raise "Remove this patch if Secondbase is no longer being used!" if Rails::VERSION::MAJOR > 5
if Rails.version =~ /^5.2/ && (Rails.env.development? || Rails.env.test?)
module ActiveRecord
module ConnectionAdapters
class AbstractAdapter
def build_fixture_sql(fixtures, table_name)
# [insert rest of unchanged method here]
# ...
#
# last line:
# manager.to_sql # original
manager.to_sql OpenStruct.new(connection: self) # fix
end
end
end
end
end
For now we are committing this patch directly to the affected app, may add it Secondbase for legacy support at a later date if there's a need for it.
On upgrade our app to Rails 5.2, all tests started failing on the following error:
To isolate the issue away from the app (monkey-patches, bad fixtures), I was able to recreate the issue in a pristine Rails 5.2 app running SecondBase with MySQL and PostgreSQL.
The issue is the same as the one encountered with multiverse: https://github.com/ankane/multiverse/issues/34
As described in the above issue, Rails 5.2 switches to loading fixtures using
ActiveRecord::ConnectionAdapters::DatabaseStatements#insert_fixtures_set
, which in turn relies onActiveRecord::ConnectionAdapters::DatabaseStatements#build_fixture_sql
. This directly builds a bulk insert statement usingArel::Table
, which defaults to usingActiveRecord::Base
. The result in our application is that the generated SQL is for Oracle (primary DB) instead of MySQL (Secondbase DB).I tried to create a Secondbase-owned inheritor of
Arel::Table
with itsengine
set toSecondBase::Base
, but this critical block of code still produced Oracle SQL, even with this new class:The only apparent solution then would be to patch
build_fixture_sql
to dynamically change theengine
ofArel::Table
depending on which table it's building for. That seems like a suboptimal solution in many ways: high effort, complicated and brittle patch, and potential for unpredictable/subtle bugs. All even less appealing considering Rails 6.0 adds multi-database support.Instead, I think the solution we'll move forward with was the "temporary" adapter patch I devised to clean up the SQL before insert:
This cleans up the SQL just before insert. This works for Oracle -> MySQL, but would need to be adapted for other database pairings. In any case, the patch will be cleaned up, restricted to the test environment, etc.