PG::DatatypeMismatch: ERROR:  column "foo" cannot be cast automatically to type <barbaz> precision

:::note warn 
**注記**
この記事の投稿者は初学者であり、自身の学習の備忘録目的で記事投稿を行なっております。
情報の正確さには最大限注意をしておりますが、もし誤り等がございましたらお知らせいただけますと幸いです。
:::
  
ご覧いただきありがとうございます！
 **Ito** と申します！
Ruby on Rails初学者です。

RailsでのWebアプリを作成中、データベースにテーブルを作成した際に、設定するデータ型を間違えてしまいました。
それ自体は「あるある」だと思うのですが、`migration`ファイルを作成してデータ型を変更しようとした際に**ほんのちょびっとだけ**ハマってしまったので備忘録として残します。
 
## 動作環境
開発環境
 - Rails 7.1.3.2
 - サーバー:localhost接続
 - データベース:PostgreSQL

## エラーの詳細

### エラーメッセージ
```
== 20240401****** ChangeLatAndLngToFloatInSpots: migrating ====================
-- change_column(:spots, :lat, :float)
bin/rails aborted!
StandardError: An error has occurred, this and all later migrations canceled: (StandardError)

PG::DatatypeMismatch: ERROR:  column "lat" cannot be cast automatically to type double precision
HINT:  You might need to specify "USING lat::double precision".
/Users ... /db/migrate/20240401******_change_lat_and_lng_to_float_in_spots.rb:3:in `change'

Caused by:
ActiveRecord::StatementInvalid: PG::DatatypeMismatch: ERROR:  column "lat" cannot be cast automatically to type double precision (ActiveRecord::StatementInvalid)
HINT:  You might need to specify "USING lat::double precision".
```

### どんな時に発生したか
1.Railsジェネレータを用いて、`lat`(緯度)と`lng`(経度)属性を持つ`Spot`モデルを作成
  ```rb
  rails generate model Spot lat:string lng:string 
  ```
2.`lat`カラムと`lng`カラムを`string`型から`float`型に変更するため、Railsジェネレータを用いてマイグレーションファイルを作成する
```rb
rails generate migration ChangeLatAndLngToFloatInSpots
```

3.作成されたマイグレーションファイルの中身を書いていく
```rb:20240401******_change_lat_and_lng_to_float_in_spots
class ChangeLatAndLngToFloatInSpots < ActiveRecord::Migration[7.1]
  def change
    change_column :spots, :lat, :float
    change_column :spots, :lng, :float
  end
end
```

4.`rails db:migrate`を実行する
5.先ほどのエラーメッセージ


### 調べた結果
PostgreSQLでは、特定の型から別の型への変更を行う際に、**データの解釈が自明でない場合（例えば`string`から`float`への変更など）、どのように変換するかを明示的に指定する必要がある**ようです。

エラーメッセージには、
**「`lat`カラムを`double precision`（Railsで言う`float`型に相当する）に自動でキャスト(型変換)できないから、明示的なキャスト方法を指定する必要があるよ」** という意味が込められています。
 
※PostgreSQLのみ発生するのかは、不明です。

## こう解決した
次のようにマイグレーションファイルを書き換えてから`rails db:migrate`を実行することで、無事に`lat`カラムと`lng`カラムの型を`float`型に変更することができました。

```rb:20240401******_change_lat_and_lng_to_float_in_spots
class ChangeLatAndLngToFloatInSpots < ActiveRecord::Migration[7.1]
  def up
    change_column :spots, :lat, 'float USING CAST(lat AS float)'
    change_column :spots, :lng, 'float USING CAST(lng AS float)'
  end

  def down
    change_column :spots, :lat, :string
    change_column :spots, :lng, :string
  end
end
```
マイグレーションファイルの中で、`change`メソッドでなく`up`メソッドと`down`メソッドを使います。(`up`に失敗した際に、`down`の内容にロールバックするため)
その際、`up`メソッド内の`change_column`メソッドと組み合わせて`using`オプションを使用し、明示的に`cast`を指示しています。

`up/down`メソッドについては、Railsガイドをご参照ください。
[3.12 up/downメソッドを使う](https://railsguides.jp/active_record_migrations.html#up-down%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E4%BD%BF%E3%81%86:~:text=%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82-,3.12%20up/down%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E4%BD%BF%E3%81%86,-change%E3%81%AE%E4%BB%A3%E3%82%8F%E3%82%8A)

## 振り返り
Railsで開発をしている時は、型についてはほとんど考える機会がないため、複雑ではありませんが新しい体験ができたと思います✨
がしかし、もっと注意深くRailsジェネレーターにコマンドを打ち込んでいれば、こんな面倒なことにはならな買ったので、今後は実行前に見直すようにしたいと思います！