Arel not picking up on ActiveRecord establish_connection settings #880

Closed
lighthouse-import opened this Issue May 16, 2011 · 4 comments

Comments

Projects
None yet
1 participant

Imported from Lighthouse. Original ticket at: http://rails.lighthouseapp.com/projects/8994/tickets/6491
Created by Sebastian Vogelsang - 2011-04-14 10:17:34 UTC

Hi there,

in my 3.0.4 application I'm trying to set an individual databases for some of my models in the following way within the environment.rb:

models.each do |klass|
  next unless klass < ActiveRecord::Base
  klass.establish_connection "processing_#{Rails.env}"
end

When doing a Channel.connection.instance_eval {@config[:database]} afterwards (Channel being one of my models), all appears correct. However, when doing a Channel.arel_engine.connection.instance_eval {@config[:database]}, I get the standard ActiveRecord database connection returned. It appears as if Arel does not pick up on the modified active_record connection. The funny thing is that this only appears for some of my models. Others return the correct database for both connection and arel_engine.

Another thing to notice is that while Channel.first works fine, I get the following error, when doing a Channel.find(1):

NoMethodError: undefined method `eq' for nil:NilClass

Regards,

Sebastian

Imported from Lighthouse.
Comment by Sebastian Vogelsang - 2011-03-01 18:21:38 UTC

ok, I got it working by changing my code to the following:

models.each do |klass|
  next unless klass < ActiveRecord::Base
  klass.establish_connection "processing_#{Rails.env}"
  klass.class_eval do
    @arel_engine = klass
    @arel_table = Arel::Table.new(klass.table_name, klass)
    @relation = ActiveRecord::Relation.new(klass, @arel_table)
  end
end

I hope this is not the way we have to do it from now?!? Shouldn't establish_connection take care of setting up the AREL Relations appropriately?

Sebastian

Imported from Lighthouse.
Comment by atomgas - 2011-03-22 11:20:41 UTC

HasAndBelongsToMany did not work with multiple database, this fixed it for me:

module ActiveRecord
  # = Active Record Has And Belongs To Many Association
  module Associations
    class HasAndBelongsToManyAssociation < AssociationCollection
      protected
        def insert_record(record, force = true, validate = true)
          if record.new_record?
            if force
              record.save!
            else
              return false unless record.save(:validate => validate)
            end
          end

          if @reflection.options[:insert_sql]
            @owner.connection.insert(interpolate_and_sanitize_sql(@reflection.options[:insert_sql], record))
          else
            # IMPORTANT!!!
            # here active record does not pass the engine to Arel::Table and thus Arel::Table uses Table.engine = ActiveRecord::Base
            # IMPORTANT!!!
            relation   = Arel::Table.new(@reflection.options[:join_table], @owner.class.arel_engine)
            timestamps = record_timestamp_columns(record)
            timezone   = record.send(:current_time_from_proper_timezone) if timestamps.any?


            # debugger
            attributes = Hash[columns.map do |column|
                                name = column.name
                                value = case name.to_s
                                when @reflection.primary_key_name.to_s
                                  @owner.id
                                when @reflection.association_foreign_key.to_s
                                  record.id
                                when *timestamps
                                  timezone
                                else
                                  @owner.send(:quote_value, record[name], column) if record.has_attribute?(name)
                                end

                                [relation[name], value] unless value.nil?
            end]
            relation.insert(attributes)
          end

          return true
        end

        def delete_records(records)
          if sql = @reflection.options[:delete_sql]
            records.each { |record| @owner.connection.delete(interpolate_and_sanitize_sql(sql, record)) }
          else
            # IMPORTANT!!!
            # here active record does not pass the engine to Arel::Table and thus Arel::Table uses Table.engine = ActiveRecord::Base
            # IMPORTANT!!!            
            relation = Arel::Table.new(@reflection.options[:join_table], @owner.class.arel_engine)
            relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
              and(relation[@reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
            ).delete
          end
        end

    end
  end
end

Imported from Lighthouse.
Comment by Sebastian Vogelsang - 2011-04-12 10:52:22 UTC

anyone? This is a rather serious issue! I can't imagine that nobody else has encountered these problems when connecting to multiple databases?!

Imported from Lighthouse.
Comment by Ibrahim - 2011-04-13 11:55:56 UTC

You don't need to do this in the environment.rb If you want separate connections in your models, use the ActiveRecord::Base.establish_connection method that takes a options hash. Place your database configuration in the appropriate config file and use that in your model:

class User < ActiveRecord::Base
  establish_connection(
    :adapter => "mysql"
    :host    => "localhost"
  )
end

If you want to use your YAML, it is available as a symbol within ActiveRecord::Base like so:

class User < ActiveRecord::Base
  establish_connection :from_yaml
end

And to keep things dry, you can setup a parent model that establishes the connection to the other database and let your models inherit from it, since establish_connection stays in the family!

class Person < ActiveRecord::Base
  establish_connection :private_government_database
end

class User < Person
end

class Admin < User
end

Hope this helps you ;-)

@hisas hisas pushed a commit to hisas/rails that referenced this issue May 9, 2017

@jeremy jeremy Merge pull request #880 from jeremyevans/columnar
require mime/types/columnar if available for large memory savings
6a8be76
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment