GoでSQLを書いて実行するとき、素のdatabase/sqlだけだとさすがに心もとないのでsqlxなどのライブラリを使ってDBの行をstructにマッピングすると思います。db
struct tagでマッピング元のカラム名を指定できるのが便利ですね。
一方で、このstructを定義する作業ですが、テーブルのカラムが多いと大変だし、typoしていたので直して再チャレンジする……ということが往々にしてあると思います。
こういうときのためにstruct定義を生成するツールを書いてみました。
go install github.com/utgwkk/rowstructgen@latest
を実行したら使えるようになると思います。今のところMySQLにしか対応していません (普段はMySQLしか使っていないため)。main.goに全ての実装が書いてあってひどい感じなので、気が向いたらなおします。
以下のようなDBスキーマが schema.sql というファイル名で用意されていたとします。論理削除には目をつぶってください。
CREATE TABLE `users` ( `id` BIGINT UNSIGNED NOT NULL, `name` VARCHAR(255) NOT NULL, `deleted_at` DATETIME NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
このようなスキーマに対して、rowstructgenで users
テーブルに対応するGoのstruct定義を生成するには以下のようにします。
$ rowstructgen -schema schema.sql -table users -struct User -package row -out row/user.go
こうすると row/user.go に以下のようなファイルが生成されます。
package row import "time" type User struct { Id uint64 `db:"id"` Name string `db:"name"` DeletedAt *time.Time `db:"deleted_at"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` }
ISUCONで用意されているようなDBスキーマならこれぐらいでうまく動くのではないでしょうか。ちゃんとやるならALTER TABLE文をシミュレートする必要がありそうですが、自分の目的を満たすにはこれでじゅうぶんかなーと思っています。
みどころ
sqldefをSQLパーサーとして使う
sqldefというDBスキーマの管理ツールがあります。sqldefについては以下のブログ記事などを読んでください。
sqldefはSQLのパーサーを内包しているので、これを使わせてもらいます。これによってDBスキーマをなめてコード生成を行うことに集中できました。
MySQLの型をGoの型にマッピングする
ベタッと書いてあってひどい感じですが、書いてあります。
https://github.com/utgwkk/rowstructgen/blob/bc3221a6d169a4f5c51a0538194bc7b8dfb49b10/main.go#L67
ENUMはstring型にしておくので、必要に応じて独自の型にマップさせましょう、というスタンスです (凝ったことをやりたくないから)。JSONはひとまずbyte型にマッピングしていますがこれも諸説あると思います。ところでJSONってbyteにマッピングできるんでしたっけ?