From 5d0ca7462255b7d52a007cfe5bbc7e1bf67c3f79 Mon Sep 17 00:00:00 2001
From: Yves Senn <yves.senn@gmail.com>
Date: Thu, 28 Feb 2013 10:05:13 +0100
Subject: Support PostgreSQL specific column types when using `change_table`.

Closes #9480.

We use `TableDefinition` for `#create_table` and `Table` for `#change_table`.
The PostgreSQL sepcifc types were only defined on `TableDefinition` so I
also added them to `Table`.
---
 activerecord/CHANGELOG.md                                | 12 ++++++++++++
 .../connection_adapters/abstract/schema_statements.rb    | 12 ++++++++----
 .../connection_adapters/postgresql_adapter.rb            | 16 ++++++++++++++--
 .../test/cases/adapters/postgresql/hstore_test.rb        | 15 +++++++++++++++
 activerecord/test/cases/adapters/postgresql/json_test.rb | 15 +++++++++++++++
 5 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 496f902979..a5d74418c4 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,17 @@
 ## Rails 4.0.0 (unreleased) ##
 
+*   Support PostgreSQL specific column types when using `change_table`.
+    Fixes #9480.
+
+    Example:
+
+        change_table :authors do |t|
+          t.hstore :books
+          t.json :metadata
+        end
+
+    *Yves Senn*
+
 *   Revert 408227d9c5ed7d, 'quote numeric'. This introduced some regressions.
 
     *Steve Klabnik*
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 9bae880024..0cce8c7596 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -156,7 +156,7 @@ module ActiveRecord
       #
       # See also TableDefinition#column for details on how to create columns.
       def create_table(table_name, options = {})
-        td = table_definition
+        td = create_table_definition
         td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
 
         yield td if block_given?
@@ -298,10 +298,10 @@ module ActiveRecord
       def change_table(table_name, options = {})
         if supports_bulk_alter? && options[:bulk]
           recorder = ActiveRecord::Migration::CommandRecorder.new(self)
-          yield Table.new(table_name, recorder)
+          yield update_table_definition(table_name, recorder)
           bulk_change_table(table_name, recorder.commands)
         else
-          yield Table.new(table_name, self)
+          yield update_table_definition(table_name, self)
         end
       end
 
@@ -727,9 +727,13 @@ module ActiveRecord
         end
 
       private
-      def table_definition
+      def create_table_definition
         TableDefinition.new(self)
       end
+
+      def update_table_definition(table_name, base)
+        Table.new(table_name, base)
+      end
     end
   end
 end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 2bb2557efd..b39eb058ae 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -263,7 +263,7 @@ module ActiveRecord
         attr_accessor :array
       end
 
-      class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
+      module ColumnMethods
         def xml(*args)
           options = args.extract_options!
           column(args[0], 'xml', options)
@@ -325,6 +325,10 @@ module ActiveRecord
         def json(name, options = {})
           column(name, 'json', options)
         end
+      end
+
+      class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
+        include ColumnMethods
 
         def column(name, type = nil, options = {})
           super
@@ -344,6 +348,10 @@ module ActiveRecord
         end
       end
 
+      class Table < ActiveRecord::ConnectionAdapters::Table
+        include ColumnMethods
+      end
+
       ADAPTER_NAME = 'PostgreSQL'
 
       NATIVE_DATABASE_TYPES = {
@@ -884,9 +892,13 @@ module ActiveRecord
           $1.strip if $1
         end
 
-        def table_definition
+        def create_table_definition
           TableDefinition.new(self)
         end
+
+        def update_table_definition(table_name, base)
+          Table.new(table_name, base)
+        end
     end
   end
 end
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index 6640f9b497..ad98d7c8ce 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -65,6 +65,21 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
     assert_equal :hstore, @column.type
   end
 
+  def test_change_table_supports_hstore
+    @connection.transaction do
+      @connection.change_table('hstores') do |t|
+        t.hstore 'users', default: ''
+      end
+      Hstore.reset_column_information
+      column = Hstore.columns.find { |c| c.name == 'users' }
+      assert_equal :hstore, column.type
+
+      raise ActiveRecord::Rollback # reset the schema change
+    end
+  ensure
+    Hstore.reset_column_information
+  end
+
   def test_type_cast_hstore
     assert @column
 
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index d64037eec0..6fc08ae4f0 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -31,6 +31,21 @@ class PostgresqlJSONTest < ActiveRecord::TestCase
     assert_equal :json, @column.type
   end
 
+  def test_change_table_supports_json
+    @connection.transaction do
+      @connection.change_table('json_data_type') do |t|
+        t.json 'users', default: '{}'
+      end
+      JsonDataType.reset_column_information
+      column = JsonDataType.columns.find { |c| c.name == 'users' }
+      assert_equal :json, column.type
+
+      raise ActiveRecord::Rollback # reset the schema change
+    end
+  ensure
+    JsonDataType.reset_column_information
+  end
+
   def test_type_cast_json
     assert @column
 
-- 
cgit v1.2.3