GORM + PostgreSQL で double precision を使う場合は float8 を指定すると良さそう

GORM で PostgreSQL を利用する場合の小ネタです。

執筆時点 (2024/04/06) での GORM のバージョンは v1.25.9、GORM PostgreSQL Driver のバージョンは v1.5.7 です。将来のバージョンでは挙動が変わる可能性があるのでご了承ください。

先にまとめ

  • GORM + PostgreSQL でモデルの float64 型のフィールドに gorm:"type:double precision" を指定すると、AutoMigrate 実行時に余計な ALTER TABLE が発生してしまうことがある
  • gorm:"type:float8" を指定することでこの問題を回避できそう

準備

以下のような compose.yaml ファイルを用意して、Docker Compose で PostgreSQL を起動できるようにしておきます。

# compose.yaml
services:
  postgres:
    image: postgres:16.2
    container_name: postgres
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=example
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8
      - LANG=C
      - TZ=Asia/Tokyo
      - PGTZ=Asia/Tokyo
    ports:
      - "127.0.0.1:5432:5432"
    restart: always

本題

GORM で float64 型のフィールドを持つモデルを定義して AutoMigrate すると、PostgreSQL 上では decimal (numeric) 型のカラムを持ったテーブルが作成されます。

// main.go
package main

import (
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

const (
    DSN = "host=localhost port=5432 user=postgres password=password dbname=example sslmode=disable TimeZone=Asia/Tokyo"
)

type Sample struct {
    Column float64
}

func main() {
    db, err := gorm.Open(postgres.Open(DSN))
    if err != nil {
        panic("failed to connect database")
    }

    // デバッグモードを有効にして AutoMigrate を実行
    if err := db.Debug().AutoMigrate(&Sample{}); err != nil {
        panic("failed to migrate database")
    }
}

実行してみます。

$ docker compose up -d
$ go run main.go

ログを見ると、確かに decimal 型のカラムを持ったテーブルが作成されていることがわかります。

[7.177ms] [rows:0] CREATE TABLE "samples" ("column" decimal)

しかし、場合によっては decimal ではなく PostgreSQL の倍精度浮動小数点データ型 (double precision) で格納したいこともあると思います。

そこで、gorm:"type:double precision" を指定してみます。

// main.go

// ...

type Sample struct {
    Column float64 `gorm:"type:double precision"`
}

// ...
$ docker compose down
$ docker compose up -d
$ go run main.go

ログを見ると、今度は double precision 型のカラムを持ったテーブルが作成されていることがわかります。

[3.314ms] [rows:0] CREATE TABLE "samples" ("column" double precision)

しかし、この状態でもう一度 AutoMigrate を実行してログを見てみると、ALTER TABLE が実行されてしまっていることがわかります。

$ go run main.go
[1.418ms] [rows:0] ALTER TABLE "samples" ALTER COLUMN "column" TYPE double precision USING "column"::double precision

この不要な ALTER TABLE は、double precision の別名である float8 を用いることで回避できます。

// main.go

// ...

type Sample struct {
    Column float64 `gorm:"type:float8"`
}

// ...
$ docker compose down
$ docker compose up -d
$ go run main.go
$ go run main.go

この場合は二度目の AutoMigrate で不要な ALTER TABLE が発生しません。

また、単精度浮動小数点データ型 (real) の場合も float4 を指定することで同様の問題を回避できるようです。

余談

最近月曜日はリモートワークにしていたのですが、4/1 には新入社員の方々が入社されるということでオフィスに出社したところ、よりにもよってコーヒーポットをひっくり返してしまいました。ごめんなさい。

初出社日にコーヒー(ポット)をひっくり返してしまった全国の新入社員の皆さん、あまり落ち込みすぎないで頑張ってください。

会津ラボはそれでもコーヒーが好きなコーヒー大好き人類を募集しています。

コメントを残す

メールアドレスが公開されることはありません。*がついている欄は必須項目です。

日本語が含まれない投稿は無視されますのでご注意ください。