🪐

SQLboiler で SQLite の日付フィールドを扱うポイント

2024/05/12に公開

はじめに

Go でデータベースを操作するライブラリの一つに SQLboiler があります。

SQLboiler は database first と呼ばれる ORM ジェネレーターで、データ構造から自動的に ORM を生成できます。

https://github.com/volatiletech/sqlboiler

本記事では、SQLite で SQLboiler を利用して ORM を生成する際の日付フィールドの扱いについて、ハマったポイントと対処方法を説明します。

SQLite で日付フィールドを扱う上での SQLboiler の注意点

SQLite には日付型を扱うフィールドはありません。

https://arc.net/l/quote/jkhtsmkj

そのため日付データは TEXT 型として扱い、 Date And Time Functions を利用して定義します。

例えば created_at TEXT NOT NULL DEFAULT (DATETIME('now','localtime'))のように記述します。

さて、上記のようにテーブルのフィールドを定義したデータベースから SQLboiler で ORM を生成すると、created_at や updated_at フィールドはデータベースと同じ string 型で変換されます。

一方 SQLboiler では created_at や updated_at というフィールド名の場合はデフォルトでは time.Now() で自動で timestamp を行う ORM が生成されます。

If your generated SQLBoiler models package can find columns with the names created_at or updated_at it will automatically set them to time.Now() in your database, and update your object appropriately.
https://github.com/volatiletech/sqlboiler/blob/master/README.md#automatic-createdatupdatedat

この時、生成された ORM では created_at や updated_at を time.Time 型として扱ってしまいます。
そのため、型の不整合でコンパイルエラーになってしまいます。

  • 生成されるコードの例
...
currTime := time.Now().In(boil.GetLocation())

// o.CreatedAt が string 型なのでコンパイルエラーになる
if queries.MustTime(o.CreatedAt).IsZero() { 
    queries.SetScanner(&o.CreatedAt, currTime)
}
...

SQLite の created_at や updated_at を SQLboiler で扱う方法

型の不整合をどう扱うかについて、対応策を紹介します。

対応策1: auto timestamp を無効化する

--no-auto-timestampsオプションを使用して auto timestamp を無効化することができます。

ORM で created_at や updated_at フィールドを time.Time 型として扱わないようになるので、型の不整合によるエラーがなくなります。

没案: カスタムフィールドを定義する

カスタムフィールドを定義する方法ではエラーになりました。

SQLboiler の設定でドライバーが定義する型を上書きすることで、created_at や updated_at フィールドの型を time.Time に変更することができます。

https://github.com/volatiletech/sqlboiler/blob/master/README.md#types

例えば sqlboiler.toml に以下のように記述します。

...
[[types]]
[types.match]
name = "created_at" 
[types.replace]
type = "time.Time"
...

これで ORM の生成はうまくいくのですが、実際にデータベースと接続する際にエラーになりました。
sql driver 上は string として扱うため、ORM 上の 構造体を time.Time としたとしてもうまくいかないようでした。

  • 実際に発生したエラー
db: failed to execute a one query for repositories: failed to bind pointers to obj: sql: Scan error on column index 2, name \"created_at\": unsupported Scan, storing driver.Value type string into type *time.Time

まとめ

SQLboilerはデータベースのスキーマからコードを自動生成できる便利なツールですが、SQLiteを利用する場合は日付データの扱いに注意が必要です。

本記事では、SQLiteでSQLboilerを利用してORMを生成する際の注意点と対応策について説明しました。

Discussion