gormのPluckの「0 rows affected or returned」


開発業務でgorm(v1)を使用しています。

以下のように、ログ出力をONにしてPluckで検索するtest.goを実行すると、fruitsテーブルに格納されている結果が取得できているにも関わらず、0 rows affected or returnedと表示されます。

test.go
// ...
f := fruit{}
db = db.Model(&f).Pluck("name", &name)
fmt.Println(name)
// ...
output
[2020-08-20 01:20:03]  [3.66ms]  SELECT name FROM `fruits`    
[0 rows affected or returned ] 
[みかん りんご なし もも さくらんぼ]

gormのログを出力を確認しながら実装などしてるので、ログを見て一瞬「データが取れてないのか!?」とギョッとすることがあります。
こうなっているのには何か意味があるのだろうか…と思ったのでgormの中身を見てみました。

検索結果の行数がちゃんと表示されるFind()などでは、内部でcallCallbacksを呼び出し、

github.com/jinzhu/gorm/main.go
// Find find records that match given conditions
func (s *DB) Find(out interface{}, where ...interface{}) *DB {
    return s.NewScope(out).inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
}

最終的にcallback_query.goの中で取得した行数を数えているようです。

github.com/jinzhu/gorm/callback_query.go

...
for rows.Next() {
    scope.db.RowsAffected++
...

Pluckの場合はメソッドの中で、scope.goのpluckを呼び出しています。

github.com/jinzhu/gorm/main.go
func (s *DB) Pluck(column string, value interface{}) *DB {
    return s.NewScope(s.Value).pluck(column, value).db
}

scope.goのpluckの中身を見てみると、Findとは違い、取得した行数を数えていないようです。

github.com/jinzhu/gorm/scope.go
...
rows, err := scope.rows()
if scope.Err(err) == nil {
    defer rows.Close()
    for rows.Next() {
        elem := reflect.New(dest.Type().Elem()).Interface()
        scope.Err(rows.Scan(elem))
        dest.Set(reflect.Append(dest, reflect.ValueOf(elem).Elem()))
    }

    if err := rows.Err(); err != nil {
        scope.Err(err)
    }
}
...

ではここで行数を数えるようにすれば検索できた行数をログに出せるようになるのか…と考えたところでv2の方のプルリクに修正が上がっているのに気づきました。

rows affected or returned is zero when use Pluck()/Count()/Preload(many2many)

scope.goのpluckに行数を数える処理が追加されてました。
ただのバグで、CountPreloadも同様に行数が数えられてなかったみたいです。

gormはちょこちょこバグっぽい挙動があるので、不思議に思ったらときどき中身を見ながらうまく付き合っていきたいですね。。