From 1745e905a314754b65eabc5fa28671c80796f09f Mon Sep 17 00:00:00 2001
From: Ryuta Kamizono <kamipo@gmail.com>
Date: Fri, 25 Jan 2019 22:01:07 +0900
Subject: Allow changing text and blob size without giving the `limit` option

In MySQL, the text column size is 65,535 bytes by default (1 GiB in
PostgreSQL). It is sometimes too short when people want to use a text
column, so they sometimes change the text size to mediumtext (16 MiB) or
longtext (4 GiB) by giving the `limit` option.

Unlike MySQL, PostgreSQL doesn't allow the `limit` option for a text
column (raises ERROR: type modifier is not allowed for type "text").
So `limit: 4294967295` (longtext) couldn't be used in Action Text.

I've allowed changing text and blob size without giving the `limit`
option, it prevents that migration failure on PostgreSQL.
---
 .../connection_adapters/abstract_mysql_adapter.rb              |  5 +++--
 .../connection_adapters/mysql/schema_definitions.rb            | 10 ++++++++++
 .../active_record/connection_adapters/mysql/schema_dumper.rb   | 10 ++++++++--
 3 files changed, 21 insertions(+), 4 deletions(-)

(limited to 'activerecord/lib')

diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 415a0576c4..7b69a63f6e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -29,7 +29,7 @@ module ActiveRecord
       NATIVE_DATABASE_TYPES = {
         primary_key: "bigint auto_increment PRIMARY KEY",
         string:      { name: "varchar", limit: 255 },
-        text:        { name: "text", limit: 65535 },
+        text:        { name: "text" },
         integer:     { name: "int", limit: 4 },
         float:       { name: "float", limit: 24 },
         decimal:     { name: "decimal" },
@@ -37,7 +37,8 @@ module ActiveRecord
         timestamp:   { name: "timestamp" },
         time:        { name: "time" },
         date:        { name: "date" },
-        binary:      { name: "blob", limit: 65535 },
+        binary:      { name: "blob" },
+        blob:        { name: "blob" },
         boolean:     { name: "tinyint", limit: 1 },
         json:        { name: "json" },
       }
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
index 2ed4ad16ae..90bcdf3297 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
@@ -56,6 +56,16 @@ module ActiveRecord
           case type
           when :virtual
             type = options[:type]
+          when :text, :blob, :binary
+            case (size = options[:size])&.to_s
+            when "tiny", "medium", "long"
+              sql_type = @conn.native_database_types[type][:name]
+              type = "#{size}#{sql_type}"
+            else
+              raise ArgumentError, <<~MSG unless size.nil?
+                #{size.inspect} is invalid :size value. Only :tiny, :medium, and :long are allowed.
+              MSG
+            end
           when :primary_key
             type = :integer
             options[:limit] ||= 8
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
index d23178e43c..57518b02fa 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -10,6 +10,10 @@ module ActiveRecord
             spec[:unsigned] = "true" if column.unsigned?
             spec[:auto_increment] = "true" if column.auto_increment?
 
+            if /\A(?<size>tiny|medium|long)(?:text|blob)/ =~ column.sql_type
+              spec = { size: size.to_sym.inspect }.merge!(spec)
+            end
+
             if @connection.supports_virtual_columns? && column.virtual?
               spec[:as] = extract_expression_for_virtual_column(column)
               spec[:stored] = "true" if /\b(?:STORED|PERSISTENT)\b/.match?(column.extra)
@@ -37,13 +41,15 @@ module ActiveRecord
             case column.sql_type
             when /\Atimestamp\b/
               :timestamp
-            when "tinyblob"
-              :blob
             else
               super
             end
           end
 
+          def schema_limit(column)
+            super unless /\A(?:tiny|medium|long)?(?:text|blob)/.match?(column.sql_type)
+          end
+
           def schema_precision(column)
             super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
           end
-- 
cgit v1.2.3