From a2ad8f456e29f760f78333e14474868a63a22a6f Mon Sep 17 00:00:00 2001
From: Ryuta Kamizono <kamipo@gmail.com>
Date: Thu, 25 Oct 2018 10:11:31 +0900
Subject: Support default expression for MySQL

MySQL 8.0.13 and higher supports default value to be a function or
expression.

https://dev.mysql.com/doc/refman/8.0/en/create-table.html
---
 activerecord/CHANGELOG.md                                     |  8 ++++++++
 .../connection_adapters/mysql/schema_statements.rb            | 11 +++++++----
 activerecord/test/cases/defaults_test.rb                      |  7 +++++++
 activerecord/test/cases/helper.rb                             |  9 +++++++++
 activerecord/test/schema/mysql2_specific_schema.rb            |  3 +++
 5 files changed, 34 insertions(+), 4 deletions(-)

(limited to 'activerecord')

diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 94061a076d..f56551beca 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,11 @@
+*   Support default expression for MySQL.
+
+    MySQL 8.0.13 and higher supports default value to be a function or expression.
+
+    https://dev.mysql.com/doc/refman/8.0/en/create-table.html
+
+    *Ryuta Kamizono*
+
 *   Support expression indexes for MySQL.
 
     MySQL 8.0.13 and higher supports functional key parts that index
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
index 65e9a1fda5..4894fd1c08 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
@@ -106,10 +106,13 @@ module ActiveRecord
 
           def new_column_from_field(table_name, field)
             type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
-            if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(field[:Default])
-              default, default_function = nil, field[:Default]
-            else
-              default, default_function = field[:Default], nil
+            default, default_function = field[:Default], nil
+
+            if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
+              default, default_function = nil, default
+            elsif type_metadata.extra == "DEFAULT_GENERATED"
+              default = +"(#{default})" unless default.start_with?("(")
+              default, default_function = nil, default
             end
 
             MySQL::Column.new(
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index 0f957d41cf..5d02e59ef6 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -106,6 +106,13 @@ if current_adapter?(:Mysql2Adapter)
   class MysqlDefaultExpressionTest < ActiveRecord::TestCase
     include SchemaDumpingHelper
 
+    if supports_default_expression?
+      test "schema dump includes default expression" do
+        output = dump_table_schema("defaults")
+        assert_match %r/t\.binary\s+"uuid",\s+limit: 36,\s+default: -> { "\(uuid\(\)\)" }/i, output
+      end
+    end
+
     if subsecond_precision_supported?
       test "schema dump datetime includes default expression" do
         output = dump_table_schema("datetime_defaults")
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 68be685e4b..730cd663a2 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -48,6 +48,15 @@ def mysql_enforcing_gtid_consistency?
   current_adapter?(:Mysql2Adapter) && "ON" == ActiveRecord::Base.connection.show_variable("enforce_gtid_consistency")
 end
 
+def supports_default_expression?
+  if current_adapter?(:PostgreSQLAdapter)
+    true
+  elsif current_adapter?(:Mysql2Adapter)
+    conn = ActiveRecord::Base.connection
+    !conn.mariadb? && conn.version >= "8.0.13"
+  end
+end
+
 def supports_savepoints?
   ActiveRecord::Base.connection.supports_savepoints?
 end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index 499280cb0c..ccca9a2d9b 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -19,6 +19,9 @@ ActiveRecord::Schema.define do
     t.datetime :fixed_time, default: "2004-01-01 00:00:00"
     t.column :char1, "char(1)", default: "Y"
     t.string :char2, limit: 50, default: "a varchar field"
+    if supports_default_expression?
+      t.binary :uuid, limit: 36, default: -> { "(uuid())" }
+    end
   end
 
   create_table :binary_fields, force: true do |t|
-- 
cgit v1.2.3