gem ulid-railsはmysql5.7以上じゃないと構文エラーとなる

`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` でその実装がしてあります。

~~~ruby: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-mysql
> - `FROM_UNIXTIME(int)` : シンプルにunix時間を現実の `TIMESTAMP` に変換 https://dev.mysql.com/doc/refman/5.6/ja/date-and-time-functions.html#function_from-unixtime
> - `CONV(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_conv
> - `HEX` : 文字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型` の構文がおかしいのかなとも思い、

```ruby:db/migrate/add_created_at_to_users.rb
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を導入している中でハマってしまった方のお役に立てれば嬉しいです。