mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Default engine ENGINE=InnoDB
is no longer dumped to make schema more agnostic
5 years ago, I made dumping full table options at #17569, especially to dump `ENGINE=InnoDB ROW_FORMAT=DYNAMIC` to use utf8mb4 with large key prefix. In that time, omitting the default engine `ENGINE=InnoDB` was not useful since `ROW_FORMAT=DYNAMIC` always remains as long as using utf8mb4 with large key prefix. But now, MySQL 5.7.9 has finally changed the default row format to DYNAMIC, utf8mb4 with large key prefix can be used without dumping the default engine and the row format explicitly. So now is a good time to make the default engine is omitted. Before: ```ruby create_table "accounts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t| end ``` After: ```ruby create_table "accounts", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| end ``` To entirely omit `:options` option to make schema agnostic, I've added `:charset` and `:collation` table options to exclude `CHARSET` and `COLLATE` from `:options`. Fixes #26209. Closes #29472. See also #33608, #33853, and #34742.
This commit is contained in:
parent
fd8fd4ae76
commit
3c6be5e502
11 changed files with 87 additions and 78 deletions
|
@ -1,6 +1,24 @@
|
|||
* Default engine `ENGINE=InnoDB` is no longer dumped to make schema more agnostic.
|
||||
|
||||
Before:
|
||||
|
||||
```ruby
|
||||
create_table "accounts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
end
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```ruby
|
||||
create_table "accounts", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
end
|
||||
```
|
||||
|
||||
*Ryuta Kamizono*
|
||||
|
||||
* Added delegated type as an alternative to single-table inheritance for representing class hierarchies.
|
||||
See ActiveRecord::DelegatedType for the full description.
|
||||
|
||||
|
||||
*DHH*
|
||||
|
||||
* Deprecate aggregations with group by duplicated fields.
|
||||
|
|
|
@ -53,7 +53,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
create_sql << "(#{statements.join(', ')})" if statements.present?
|
||||
add_table_options!(create_sql, table_options(o))
|
||||
add_table_options!(create_sql, o)
|
||||
create_sql << " AS #{to_sql(o.as)}" if o.as
|
||||
create_sql
|
||||
end
|
||||
|
@ -106,17 +106,8 @@ module ActiveRecord
|
|||
true
|
||||
end
|
||||
|
||||
def table_options(o)
|
||||
table_options = {}
|
||||
table_options[:comment] = o.comment
|
||||
table_options[:options] = o.options
|
||||
table_options
|
||||
end
|
||||
|
||||
def add_table_options!(create_sql, options)
|
||||
if options_sql = options[:options]
|
||||
create_sql << " #{options_sql}"
|
||||
end
|
||||
def add_table_options!(create_sql, o)
|
||||
create_sql << " #{o.options}" if o.options
|
||||
create_sql
|
||||
end
|
||||
|
||||
|
|
|
@ -277,7 +277,8 @@ module ActiveRecord
|
|||
if_not_exists: false,
|
||||
options: nil,
|
||||
as: nil,
|
||||
comment: nil
|
||||
comment: nil,
|
||||
**
|
||||
)
|
||||
@conn = conn
|
||||
@columns_hash = {}
|
||||
|
|
|
@ -295,9 +295,7 @@ module ActiveRecord
|
|||
#
|
||||
# See also TableDefinition#column for details on how to create columns.
|
||||
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
|
||||
td = create_table_definition(
|
||||
table_name, **options.extract!(:temporary, :if_not_exists, :options, :as, :comment)
|
||||
)
|
||||
td = create_table_definition(table_name, **extract_table_options!(options))
|
||||
|
||||
if id && !td.as
|
||||
pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
|
||||
|
@ -1379,14 +1377,18 @@ module ActiveRecord
|
|||
SchemaCreation.new(self)
|
||||
end
|
||||
|
||||
def create_table_definition(*args, **options)
|
||||
TableDefinition.new(self, *args, **options)
|
||||
def create_table_definition(name, **options)
|
||||
TableDefinition.new(self, name, **options)
|
||||
end
|
||||
|
||||
def create_alter_table(name)
|
||||
AlterTable.new create_table_definition(name)
|
||||
end
|
||||
|
||||
def extract_table_options!(options)
|
||||
options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
|
||||
end
|
||||
|
||||
def fetch_type_metadata(sql_type)
|
||||
cast_type = lookup_cast_type(sql_type)
|
||||
SqlTypeMetadata.new(
|
||||
|
|
|
@ -414,24 +414,31 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def table_options(table_name) # :nodoc:
|
||||
table_options = {}
|
||||
|
||||
create_table_info = create_table_info(table_name)
|
||||
|
||||
# strip create_definitions and partition_options
|
||||
# Be aware that `create_table_info` might not include any table options due to `NO_TABLE_OPTIONS` sql mode.
|
||||
raw_table_options = create_table_info.sub(/\A.*\n\) ?/m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
|
||||
|
||||
return if raw_table_options.empty?
|
||||
|
||||
table_options = {}
|
||||
|
||||
if / DEFAULT CHARSET=(?<charset>\w+)(?: COLLATE=(?<collation>\w+))?/ =~ raw_table_options
|
||||
raw_table_options = $` + $' # before part + after part
|
||||
table_options[:charset] = charset
|
||||
table_options[:collation] = collation if collation
|
||||
end
|
||||
|
||||
# strip AUTO_INCREMENT
|
||||
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
||||
|
||||
table_options[:options] = raw_table_options unless raw_table_options.blank?
|
||||
|
||||
# strip COMMENT
|
||||
if raw_table_options.sub!(/ COMMENT='.+'/, "")
|
||||
table_options[:comment] = table_comment(table_name)
|
||||
end
|
||||
|
||||
table_options[:options] = raw_table_options unless raw_table_options == "ENGINE=InnoDB"
|
||||
table_options
|
||||
end
|
||||
|
||||
|
|
|
@ -40,8 +40,11 @@ module ActiveRecord
|
|||
add_sql_comment!(sql.join(" "), o.comment)
|
||||
end
|
||||
|
||||
def add_table_options!(create_sql, options)
|
||||
add_sql_comment!(super, options[:comment])
|
||||
def add_table_options!(create_sql, o)
|
||||
create_sql = super
|
||||
create_sql << " DEFAULT CHARSET=#{o.charset}" if o.charset
|
||||
create_sql << " COLLATE=#{o.collation}" if o.collation
|
||||
add_sql_comment!(create_sql, o.comment)
|
||||
end
|
||||
|
||||
def add_column_options!(sql, options)
|
||||
|
|
|
@ -60,6 +60,14 @@ module ActiveRecord
|
|||
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
||||
include ColumnMethods
|
||||
|
||||
attr_reader :charset, :collation
|
||||
|
||||
def initialize(conn, name, charset: nil, collation: nil, **)
|
||||
super
|
||||
@charset = charset
|
||||
@collation = collation
|
||||
end
|
||||
|
||||
def new_column_definition(name, type, **options) # :nodoc:
|
||||
case type
|
||||
when :virtual
|
||||
|
|
|
@ -154,8 +154,8 @@ module ActiveRecord
|
|||
MySQL::SchemaCreation.new(self)
|
||||
end
|
||||
|
||||
def create_table_definition(*args, **options)
|
||||
MySQL::TableDefinition.new(self, *args, **options)
|
||||
def create_table_definition(name, **options)
|
||||
MySQL::TableDefinition.new(self, name, **options)
|
||||
end
|
||||
|
||||
def new_column_from_field(table_name, field)
|
||||
|
|
|
@ -621,8 +621,8 @@ module ActiveRecord
|
|||
PostgreSQL::SchemaCreation.new(self)
|
||||
end
|
||||
|
||||
def create_table_definition(*args, **options)
|
||||
PostgreSQL::TableDefinition.new(self, *args, **options)
|
||||
def create_table_definition(name, **options)
|
||||
PostgreSQL::TableDefinition.new(self, name, **options)
|
||||
end
|
||||
|
||||
def create_alter_table(name)
|
||||
|
|
|
@ -87,8 +87,8 @@ module ActiveRecord
|
|||
SQLite3::SchemaCreation.new(self)
|
||||
end
|
||||
|
||||
def create_table_definition(*args, **options)
|
||||
SQLite3::TableDefinition.new(self, *args, **options)
|
||||
def create_table_definition(name, **options)
|
||||
SQLite3::TableDefinition.new(self, name, **options)
|
||||
end
|
||||
|
||||
def validate_index_length!(table_name, new_name, internal = false)
|
||||
|
|
|
@ -5,6 +5,7 @@ require "support/schema_dumping_helper"
|
|||
|
||||
class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase
|
||||
include SchemaDumpingHelper
|
||||
self.use_transactional_tests = false
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
|
@ -17,29 +18,36 @@ class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase
|
|||
test "table options with ENGINE" do
|
||||
@connection.create_table "mysql_table_options", force: true, options: "ENGINE=MyISAM"
|
||||
output = dump_table_schema("mysql_table_options")
|
||||
options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
|
||||
assert_match %r{ENGINE=MyISAM}, options
|
||||
expected = /create_table "mysql_table_options", charset: "utf8mb4"(?:, collation: "\w+")?, options: "ENGINE=MyISAM", force: :cascade/
|
||||
assert_match expected, output
|
||||
end
|
||||
|
||||
test "table options with ROW_FORMAT" do
|
||||
@connection.create_table "mysql_table_options", force: true, options: "ROW_FORMAT=REDUNDANT"
|
||||
output = dump_table_schema("mysql_table_options")
|
||||
options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
|
||||
assert_match %r{ROW_FORMAT=REDUNDANT}, options
|
||||
expected = /create_table "mysql_table_options", charset: "utf8mb4"(?:, collation: "\w+")?, options: "ENGINE=InnoDB ROW_FORMAT=REDUNDANT", force: :cascade/
|
||||
assert_match expected, output
|
||||
end
|
||||
|
||||
test "table options with CHARSET" do
|
||||
@connection.create_table "mysql_table_options", force: true, options: "CHARSET=utf8mb4"
|
||||
@connection.create_table "mysql_table_options", force: true, options: "CHARSET=latin1"
|
||||
output = dump_table_schema("mysql_table_options")
|
||||
options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
|
||||
assert_match %r{CHARSET=utf8mb4}, options
|
||||
expected = /create_table "mysql_table_options", charset: "latin1", force: :cascade/
|
||||
assert_match expected, output
|
||||
end
|
||||
|
||||
test "table options with COLLATE" do
|
||||
@connection.create_table "mysql_table_options", force: true, options: "COLLATE=utf8mb4_bin"
|
||||
output = dump_table_schema("mysql_table_options")
|
||||
options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
|
||||
assert_match %r{COLLATE=utf8mb4_bin}, options
|
||||
expected = /create_table "mysql_table_options", charset: "utf8mb4", collation: "utf8mb4_bin", force: :cascade/
|
||||
assert_match expected, output
|
||||
end
|
||||
|
||||
test "charset and collation options" do
|
||||
@connection.create_table "mysql_table_options", force: true, charset: "utf8mb4", collation: "utf8mb4_bin"
|
||||
output = dump_table_schema("mysql_table_options")
|
||||
expected = /create_table "mysql_table_options", charset: "utf8mb4", collation: "utf8mb4_bin", force: :cascade/
|
||||
assert_match expected, output
|
||||
end
|
||||
|
||||
test "schema dump works with NO_TABLE_OPTIONS sql mode" do
|
||||
|
@ -60,47 +68,10 @@ class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase
|
|||
end
|
||||
end
|
||||
|
||||
class Mysql2DefaultEngineOptionSchemaDumpTest < ActiveRecord::Mysql2TestCase
|
||||
class Mysql2DefaultEngineOptionTest < ActiveRecord::Mysql2TestCase
|
||||
include SchemaDumpingHelper
|
||||
self.use_transactional_tests = false
|
||||
|
||||
def setup
|
||||
@verbose_was = ActiveRecord::Migration.verbose
|
||||
ActiveRecord::Migration.verbose = false
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::Base.connection.drop_table "mysql_table_options", if_exists: true
|
||||
ActiveRecord::Migration.verbose = @verbose_was
|
||||
ActiveRecord::SchemaMigration.delete_all rescue nil
|
||||
end
|
||||
|
||||
test "schema dump includes ENGINE=InnoDB if not provided" do
|
||||
ActiveRecord::Base.connection.create_table "mysql_table_options", force: true
|
||||
|
||||
output = dump_table_schema("mysql_table_options")
|
||||
options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
|
||||
assert_match %r{ENGINE=InnoDB}, options
|
||||
end
|
||||
|
||||
test "schema dump includes ENGINE=InnoDB in legacy migrations" do
|
||||
migration = Class.new(ActiveRecord::Migration[5.1]) do
|
||||
def migrate(x)
|
||||
create_table "mysql_table_options", force: true
|
||||
end
|
||||
end.new
|
||||
|
||||
ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_migration).migrate
|
||||
|
||||
output = dump_table_schema("mysql_table_options")
|
||||
options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
|
||||
assert_match %r{ENGINE=InnoDB}, options
|
||||
end
|
||||
end
|
||||
|
||||
class Mysql2DefaultEngineOptionSqlOutputTest < ActiveRecord::Mysql2TestCase
|
||||
self.use_transactional_tests = false
|
||||
|
||||
def setup
|
||||
@logger_was = ActiveRecord::Base.logger
|
||||
@log = StringIO.new
|
||||
|
@ -120,6 +91,10 @@ class Mysql2DefaultEngineOptionSqlOutputTest < ActiveRecord::Mysql2TestCase
|
|||
ActiveRecord::Base.connection.create_table "mysql_table_options", force: true
|
||||
|
||||
assert_no_match %r{ENGINE=InnoDB}, @log.string
|
||||
|
||||
output = dump_table_schema("mysql_table_options")
|
||||
expected = /create_table "mysql_table_options", charset: "utf8mb4"(?:, collation: "\w+")?, force: :cascade/
|
||||
assert_match expected, output
|
||||
end
|
||||
|
||||
test "legacy migrations contain default ENGINE=InnoDB option" do
|
||||
|
@ -132,5 +107,9 @@ class Mysql2DefaultEngineOptionSqlOutputTest < ActiveRecord::Mysql2TestCase
|
|||
ActiveRecord::Migrator.new(:up, [migration], ActiveRecord::Base.connection.schema_migration).migrate
|
||||
|
||||
assert_match %r{ENGINE=InnoDB}, @log.string
|
||||
|
||||
output = dump_table_schema("mysql_table_options")
|
||||
expected = /create_table "mysql_table_options", charset: "utf8mb4"(?:, collation: "\w+")?, force: :cascade/
|
||||
assert_match expected, output
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue