Golang Gorm scope broken after upgrade from v1 to v2

Viewed 129

I was using Gorm v1. I had this scope for pagination which was working correctly:

func Paginate(entity BaseEntity, p *Pagination) func(db *gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        var totalRows int64

        db.Model(entity).Count(&totalRows)
        
        totalPages := int(math.Ceil(float64(totalRows) / float64(p.PerPage)))
        p.TotalPages = totalPages
        p.TotalCount = int(totalRows)
        p.SetLinks(entity.ResourceName())

        return db.Offset(p.Offset).Limit(p.PerPage)
    }
}

and the way I called it:

if err := gs.db.Scopes(entities.Paginate(genre, p)).Find(&gs.Genres).Error; err != nil {
        return errors.New(err.Error())
    }

again, this used to work correctly, that is until I upgraded to Gorm v2. Now I'm getting the following message:

[0.204ms] [rows:2] SELECT * FROM genres LIMIT 2 sql: expected 9 destination arguments in Scan, not 1; sql: expected 9 destination arguments in Scan, not 1[GIN] 2022/06/18 - 00:41:00 | 400 | 1.5205ms | 127.0.0.1 | GET "/api/v1/genres" Error #01: sql: expected 9 destination arguments in Scan, not 1; sql: expected 9 destination arguments in Scan, not 1

Now, I found out that the error is due to this line: db.Model(entity).Count(&totalRows) because if I remove it then my query is being correctly executed (obviously the data for TotalPages is not correct since it wasn't calculated). Going through the documentation I saw https://gorm.io/docs/method_chaining.html#Multiple-Immediate-Methods so my guess is that the connection used to get totalRows is being reused and have some residual data therefore my offset and limit query is failing. I tried to create a new session for both the count and the offset queries: db.Model(entity).Count(&totalRows).Session(&gorm.Session{})

return db.Offset(p.Offset).Limit(p.PerPage).Session(&gorm.Session{})

hoping that each one will use their own session but doesn't seem to work.

Any suggestions?

1 Answers

In case anyone needs it:

I did have to create a new session but I wasn't creating it the right way. I ended up doing:

countDBSession := db.Session(&gorm.Session{Initialized: true})
countDBSession.Model(entity).Count(&totalRows)

and that worked as expecting. So my scope now is:

// Paginate is a Gorm scope function.
func Paginate(entity BaseEntity, p *Pagination) func(db *gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {

        var totalRows int64

        // we must create a new session to run the count, otherwise by using the same db connection
        // we'll get some residual data which will cause db.Offset(p.Offset).Limit(p.PerPage) to fail.
        countDBSession := db.Session(&gorm.Session{Initialized: true})
        countDBSession.Model(entity).Count(&totalRows)

        totalPages := int(math.Ceil(float64(totalRows) / float64(p.PerPage)))
        p.TotalPages = totalPages
        p.TotalCount = int(totalRows)
        p.SetLinks(entity.ResourceName())

        return db.Offset(p.Offset).Limit(p.PerPage)
    }
}

notice that I'm using a new session to get the count via countDBSession which won't affect the latter use of the *db.Gorm parameter in return db.Offset(p.Offset).Limit(p.PerPage).Session(&gorm.Session{})

Related