bdurand / seamless_database_pool

Add support for master/slave database clusters in ActiveRecord to improve performance.
http://rdoc.info/projects/bdurand/seamless_database_pool
MIT License
224 stars 58 forks source link

Unable to create table with hstore or array columns in PostgreSQL #17

Closed t27duck closed 7 years ago

t27duck commented 10 years ago

Using ActiveRecord 4.0.2, PostreSQL, and the latest version of seamless_database_pool.

Given the following migration:

class TestItOut < ActiveRecord::Migration
  def change
    create_table :test_table do |t|
      t.hstore :hstore_column
      t.integer :int_array_column, :array => true, :default => []
      t.string :string_array_column, :array => true, :default => []
      t.timestamps
    end
  end
end

The line that tries to create the hstore column will throw this error:

undefined method `hstore' for #<ActiveRecord::ConnectionAdapters::TableDefinition:0x007fb007ff2fd0>

The two lines that make the array column, throw this error:

undefined method `array' for #<ActiveRecord::ConnectionAdapters::ColumnDefinition:0x007fd8a0ff21e0>

(If you'd like full stack traces, I'd be more than happy to provide them)

However if I use add_column to add hstore and array columns to a newly created table in the same migration or to a table that already exists, it works.

class TestItOut < ActiveRecord::Migration
  def change
    create_table :test_table do |t|
      t.timestamps
    end

    add_column :test_table, :hstore_column, :hstore, :default => {}
    add_column :test_table, :int_array_column, :integer, :array => true, :default => []
    add_column :test_table, :string_array_column, :string, :array => true, :default => []
  end
end

Taking a shot in the dark, the class in the error isn't being set as the PostgreSQL-specific TableDefinition and ColumnDefinition classes. In the ActiveRecord 4.0.2 source in lib/active_record/connection_adaptors/postgresql_adaptor.rb lines 287 and 346, you'll see that ColumnDefinition and TableDefinition have been defined just for the PostgreSQL adaptor...

class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
  attr_accessor :array
end
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
  include ColumnMethods
  ...

(Extra context: the ColumnMethods module define the special datatypes that ActiveRecord can use with PostgreSQL, which include hstore, uuid, xml, etc)

So my guess is within the scope of a create_table block, the gem is not using/has access to the correct PostgreSQL-specific classes. However it DOES work just fine using add_column. (Not the best work-around, but better than it being completely broke I guess)

I was able to get the first migration to work by adding back the ActiveRecord::ConnectionAdapters::SchemaStatements class to the override_classes array after line ~98 of lib/seamless_database_pool/active_record/seamless_database_pool_adaptor.rb

# Get a list of all methods redefined by the underlying adapter. These will be
# proxied to the master connection.
master_methods = []
override_classes = (master_connection.class.ancestors - AbstractAdapter.ancestors)

# THIS IS WHAT I ADDED
override_classes += [ActiveRecord::ConnectionAdapters::SchemaStatements] if adapter_class_name == "PostgreSQL"

override_classes.each do |connection_class|
  master_methods.concat(connection_class.public_instance_methods(false))
  master_methods.concat(connection_class.protected_instance_methods(false))
end

I have NO idea why that works or what it is that I'm added to the adaptor class; however, that's what I did to get the migration to run correctly. I choose not to submit this as a pull request because having an adapter-specific check there isn't the most cleanest thing in the world, nor did I do any other testing with it minus some smoke tests with an example app to make sure the PostgreSQL-related stuff still worked in a console and various simple page. But I figured I'd throw as much information at you as I could regarding this issue.

Let me know if you'd like any other information. Beyond that, the gem seems to be working fine.