Rails
で、 Devise
の users
テーブルに ulid
を実装するところでハマったので同じ方がいれば。
ULID
について - https://qiita.com/kai_kou/items/b4ac2d316920e08ac75a
ulid-rails
- https://github.com/k2nr/ulid-rails
バージョン
- rails: 5.2
- ruby: 2.6
- mysql: 5.6 => 5.7
忙しい方
Mysql
のバージョンを 5.7
にすればオッケーです。
結論はこちら
気づくまでの流れ
コード解説
ULID
はタイムスタンプ部とランダム部でできているため、時刻情報を持っています。
その情報を created_at
を GeneratedColumn
とすることで変換しいれたいです。
ulid-rails
では lib/ulid/rails/patch.rb
でその実装がしてあります。
module ULID
module Rails
module Patch
module Migrations
def virtual_ulid_timestamp(timestamp_column_name, ulid_column_name)
virtual timestamp_column_name,
type: :datetime,
as: "FROM_UNIXTIME(CONV(HEX(#{ulid_column_name} >> 80), 16, 10) / 1000.0)"
end
end
end
end
end
コードをざっくり解説すると、
created_at
を virtual型
とし、入力( AS
の部分)は ulid
である id
カラムをMysql
の HEX
関数で右に 80bit
シフトしたのを 16進数
に変換し、それをまた 10進数
に変換し、1000
で割ったのが UNIXTIME
となっているのでそれを FROM_UNIXTIME
関数で直しています。
virtual型
について: https://www.driftingruby.com/episodes/virtual-columns-in-mysqlFROM_UNIXTIME(int)
: シンプルにunix時間を現実のTIMESTAMP
に変換 https://dev.mysql.com/doc/refman/5.6/ja/date-and-time-functions.html#function_from-unixtimeCONV(n, from_base, to_base)
:n
をfrom_base
からto_base
の進数に変換。今回は16進数から10進数に変換。 https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_convHEX
: 文字1文字ずつ16進数に変換 https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_hex>>
: (BIGINT
)数値またはバイナリ文字列を右にシフト。今回は80bitシフト https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html#operator_right-shift
原因
ただし、エラー文がこの AS の部分で構文エラーとなってしまいます。
StandardError: An error has occurred, all later migrations canceled:
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS (FROM_UNIXTIME(CONV(HEX(id >> 80), 16, 10) / 1000.0)))' at line 1: CREATE TABLE `users` (`id` varbinary(16) NOT NULL PRIMARY KEY, `email` varchar(255) DEFAULT '' NOT NULL, `encrypted_password` varchar(255) DEFAULT '' NOT NULL, `reset_password_token` varchar(255), `reset_password_sent_at` datetime, `remember_created_at` datetime, `sign_in_count` int DEFAULT 0 NOT NULL, `current_sign_in_at` datetime, `last_sign_in_at` datetime, `current_sign_in_ip` varchar(255), `last_sign_in_ip` varchar(255), `updated_at` datetime, `deleted_at` datetime, `created_at` datetime AS (FROM_UNIXTIME(CONV(HEX(id >> 80), 16, 10) / 1000.0)))
のうちの
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS (FROM_UNIXTIME(CONV(HEX(id >> 80), 16, 10) / 1000.0)))' at line 1
ここより、「Mysqlのバージョンによって構文のエラー起きとるよ」ってことがわかります。
mysql --version
では 5.7
と表示されており、MYSQLドキュメントを確認しても 5.7
では全ての関数が存在しており問題ありません。
ActiveRecord
の virtual型
の構文がおかしいのかなとも思い、
class AddCreatedAtToUsers < ActiveRecord::Migration[5.2]
def up
execute "ALTER TABLE users ADD COLUMN created_at datetime AS (
from_unixtime(conv(hex(`id` >> 80),16,10) / 1000.0)
)"
end
def down
remove_column :users, :created_at
end
end
このように直打ちのSQLも試してみましたがうまくいかず・・・・。
気付いたきっかけは SequelPro
でアプリのDatabaseに接続してみたら上のタブ部分に( MYSQL5.6~~
)と表示されていたからでした。
このプロジェクトだけなぜかMYSQL5.6が参照されているという・・・・。
結論
以下のコマンドを打って MYSQL
のバージョンを 5.7
に変更することで解決します。
$ brew uninstall mysql
$ brew install mysql@5.7
$ mysql.server start
$ mysql --version
$ gem uninstall mysql
$ bundle
$ rails db:migrate
(自動立ち上げの設定はこちら - https://qiita.com/rinkun/items/c1649bcbe9a79bf2b07e)
以上です、自分と同じようにrailsにULIDを導入している中でハマってしまった方のお役に立てれば嬉しいです。