Your Question

I want to delete the existing record in a transaction after error creation. In the document, it is said that the transaction will be rolled back if an error is returned. But in the example below, the transaction is aborted due to an error in the creation, not after returns an error

func (l likeGorm) CreateOrDelete(ctx context.Context, like *entity.Like) error {
    return l.db.Transaction(func(tx *gorm.DB) error {
        likeDB := l.marshal(like)

                // Create - ERROR: duplicate key value violates unique constraint "like_db_pkey" (SQLSTATE 23505)
        if err := tx.WithContext(ctx).Model(l.model).Create(likeDB).Error; err != nil {
            if isDuplicate(err) {
                                // Delete - ERROR: current transaction is aborted, commands ignored until end of transaction block (SQLSTATE 25P02)
                if err := tx.WithContext(ctx).Model(l.model).
                    Where("post_uuid = ? AND creator_uuid = ?", like.PostID, like.Creator).
                    Delete(l.model).Error; err != nil {
                    return err
                }
                return nil
            }

            return err
        }

        return nil
    })
}

The document you expected this should be explained

https://gorm.io/docs/transactions.html#Transaction

Expected answer

How can I handle a query error by another query in a transaction?

Comment From: ghost

@thanhpp Shouldn't be like this.

func BeginTransaction(db *gorm.DB) {
    if !db.Config.SkipDefaultTransaction && db.Error == nil {
        if tx := db.Begin(); tx.Error == nil {
            db.Statement.ConnPool = tx.Statement.ConnPool
            db.InstanceSet("gorm:started_transaction", true)
        } else if tx.Error == gorm.ErrInvalidTransaction {
            tx.Error = nil
        } else {
            db.Error = tx.Error
        }
    }
}

Begin() will return gorm.ErrInvalidTransaction so create's transaction don't begin.

Comment From: thanhpp

BeginTransaction @longlihale

Can you suggest a way to workaround?

Comment From: ghost

I try it and find the transaction is not aborted due to an error in the creation, after returns an error....

Comment From: ghost

BeginTransaction @longlihale

Can you suggest a way to workaround?

It should be ok to use db.Transaction

Comment From: thanhpp

BeginTransaction @longlihale

Can you suggest a way to workaround?

It should be ok to use db.Transaction

Gorm The transaction is rolled back before returning error This is a picture of my code's execution. It shows that the second query (delete) returns an error due to the transaction being aborted

Comment From: ghost

@thanhpp hello, can you provide a demo in https://github.com/go-gorm/playground ? :kissing_heart:

Comment From: thanhpp

@longlihale https://github.com/go-gorm/playground/pull/402 I still met the same problem using the playground

Comment From: jinzhu

seems like this is a feature of postgres, refer: https://www.endpointdev.com/blog/2015/02/postgres-onerrorrollback-explained/****

Comment From: OrkhanAlikhanov

How did you guys resolve it? I tried to wrap the code another transaction to create a savepoint but didn't seem to help.

Comment From: OrkhanAlikhanov

Wait, it actually helped. I just had to make sure that the code was rolling back to the previous savepoint by returning non-nil error.