Rails создает schema_migrations – Mysql2::Error: Указанный ключ слишком длинный

Я использую Rails 3.2.6 и Mysql 6.0.9 (но у меня точно такая же ошибка в MySQL 5.2.25)

Когда я создаю новую базу данных (rake db:create), а затем, когда пытаюсь загрузить схему (rake schema:load), я получаю эту ошибку:

Mysql2::Error: Specified key was too long; max key length is 767 bytes: CREATE UNIQUE INDEX `unique_schema_migrations` ON `schema_migrations` (`version`)

После нескольких часов исследований я нашел следующие решения:

1. Измените переменную MySQL innodb_large_prefix на true (или ON).

Это не сработало. Я пробовал это на своем Linux-сервере, на своем Mac и даже на Windows — это просто не работает.

2. Monkeypatch ActiveRecord::SchemaMigration.create_table

Мне не нужно, чтобы столбец version имел длину 255 (когда это UTF-8, тогда он занимает 4 * 255 = 1020 байт и превышает ограничение MySQL в 767 байт для ключей). Мне также не нужно, чтобы это было UTF-8, но все остальные таблицы в БД имеют UTF-8, и я установил utf8_czech_ci в качестве сопоставления по умолчанию.

Метод, который фактически создает таблицу schema_migrations, выглядит так:

def self.create_table
  unless connection.table_exists?(table_name)
    connection.create_table(table_name, :id => false) do |t|
      t.column :version, :string, :null => false
    end
    connection.add_index table_name, :version, :unique => true, :name => index_name
  end
end

Вы можете прочитать весь файл на Github rails/rails.

Поэтому я попытался добавить :limit => 100 к оператору t.column, но и это решение мне не удалось. Проблема в том, что я не могу загрузить этот патч, когда оригинал уже на месте. Другими словами, мой патч загружается до ActiveRecord::SchemaMigration, поэтому он перезаписывается.

Когда я помещаю это в config/initializers/patches/schema_migration.rb:

require 'active_record/scoping/default'
require 'active_record/scoping/named'
require 'active_record/base'

module ActiveRecord
  class SchemaMigration < ActiveRecord::Base
    def self.create_table
      unless connection.table_exists?(table_name)
        connection.create_table(table_name, :id => false) do |t|
          t.column :version, :string, :null => false, :limit => 100
        end
        connection.add_index table_name, :version, :unique => true, :name => index_name
      end
    end
  end
end

Он успешно загружен, но перезаписывается при загрузке исходного ActiveRecord::SchemaMigration.

Я пытался испортить ActiveSupport.on_load(:active_record), но это тоже не работает.

Есть ли способ загрузить этот файл после установки оригинального ActiveRecord::SchemaMigration и заставить этот патч работать?

У Вас есть какие-то предложения? Я могу прояснить любую часть этого вопроса, если это не имеет для вас смысла. Просто спроси у меня. Я застрял с этим слишком долго.


person Lukáš Voda    schedule 25.07.2012    source источник
comment
Рассматривали ли вы создание schema_migrations (вместе с разумным размером столбца) вручную, прежде чем вообще делать какие-либо действия с Rails?   -  person mu is too short    schedule 25.07.2012
comment
Да, я подумал об этом, но боюсь, что это не сработает с rake db:test:clone? Кроме того, это усложнит настройку на рабочих серверах/компьютерах разработчиков, потому что мне придется запустить некоторый скрипт для предварительной инициализации базы данных и создания этой таблицы.   -  person Lukáš Voda    schedule 25.07.2012


Ответы (3)


Ключ 767 должен работать. Убедитесь, что вы используете кодировку utf8, а не utf16. У меня была такая же проблема, и моя ошибка заключалась в том, что я случайно создал базу данных utf16

person dpa    schedule 30.10.2012
comment
Добавить››кодировку: utf8 в database.yml - person Atul; 06.01.2013
comment
Эта ошибка вызвана использованием кодировки utf8mb4 для базы данных, которая была создана специально для разрешения эмодзи и других символов utf8. То, что вы предлагаете, возвращается к utf8 без поддержки расширенного юникода. - person firedev; 13.03.2014
comment
Вы можете ограничить свой индексированный столбец до 191 или менее, используя utf8mb4. Проблема возникает из-за того, что в uft8mb4 maxlength указывается в байтах. - person William Weckl; 08.08.2014
comment
Работал! +1 И collation установлено на utf8_general_ci - person ray; 01.03.2019

Я предлагаю вам удалить вашу базу данных и воссоздать новую со следующими инструкциями:

 mysql -u root -p -e "CREATE DATABASE {DB_NAME} DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;"
person Tracy LOISEL    schedule 22.09.2017

У меня такая же проблема со столбцом с именем version для varchar длиной 2000

class AddVersionToUsers < ActiveRecord::Migration
  def change
    add_column :users, :version, :string, limit:2000
    add_index  :users, :version
  end
end

Я использовал этот латинский 1 1 символ 1 байт, но теперь я хочу использовать utf8mb4 1 символ 4 байта.

Настроив свою базу данных таким образом, вы можете получить индекс до 3072 байт:

docker run -p 3309:3306 --name test-mariadb -e MYSQL_ROOT_PASSWORD=Cal1mero. -d mariadb:10.2 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --innodb-large-prefix=1 --innodb-file-format=barracuda --innodb-file-per-table=1 --innodb-strict-mode=1 --innodb-default-row-format=dynamic

этого достаточно для latin_1, (будет 2000 байт), а вот для utf8mb4 будет 8000 байт. В этих ключах у вас есть несколько вариантов

Добавьте столбец с именем hash_version и реализуйте индекс для этого столбца.

Постоянный String#hash на основе только содержимого строки

Сделайте строку короче, она должна работать, но зависит от ваших потребностей

или используйте полный текст в своих миграциях, например:

class AddVersionToUsers < ActiveRecord::Migration
  def change
    add_column :users, :version, :string, limit:2000
    add_index  :users, :version, type: :fulltext
  end
end

использованная литература:

person anquegi    schedule 30.10.2019