tl;dr
- JSONカラムをマッピングするときは
[]byte
型を使わない string
型やjson.RawMessage
型を使う
起こっていたこと
Goのwebアプリケーションで、go-sql-driver/mysqlの interpolateParams=true
オプションを有効にしてテストを回したら、以下のようなエラーでテストが落ちた。
interpolateParams=true
はプレースホルダ置換を有効にするオプションである*1。
Cannot create a JSON value from a string with CHARACTER SET 'binary'.
アプリケーションのコードは interpolateParams=true
を指定しただけで他の箇所は書き換えていない。MySQLと通信するときのcharacter setに binary
を指定した覚えは全くない。何が起こっていたのか?
原因と解決策
DBの行をマップしているstructの構造をかいつまんで書くと以下の通り。JSON型のカラムを書き込むところでエラーになっていた。
type User struct { Id string `db:"id"` Data JsonColumn `db:"data"` // JSON NOT NULL } // JsonColumn は driver.Valuer, sql.Scanner を実装している // 内部的に json.Marshal, json.Unmarshal を呼んでいる type JsonColumn []byte
出てきたエラーメッセージで検索してみると以下のissueがヒットした。
JSON型のカラムをやり取りするときは []byte
型ではなく string
型や json.RawMessage
型を使うようにするといいらしい。
ということで、筆者が関わっているプロダクトではstructのフィールドの型を自由に調整できるのでこれで解決しました。
何が起こっていたのか
これだけで記事を終わっても備忘録*2としてはいいだろうけど、ついでなのでプレースホルダ置換を有効にしてエラーになったとき何が起こっていたのか見てみる。
mysqlConn
structの interpolateParams
メソッドの []byte
型に対する処理を読んでみると、文字列リテラルの前に _binary
というのを書いていて、いかにもバイナリにしていそう。
これはイントロデューサというもので、文字列リテラルのcharacter setを指示しているらしい。謎が解けましたね。