Reputation: 4156
I have this code that works.
func (r *repoPG) WithTransaction(txFunc func() error) (err error) {
tx := db.NewTx()
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
tx.Rollback()
} else if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}()
err = txFunc()
return
}
I want to avod every time to write that long defer
, so I'm trying to write a func like this:
func TxDefer(tx, err) {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
tx.Rollback()
} else if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}
using it like:
func (r *repoPG) WithTransaction(txFunc func() error) (err error) {
tx := db.NewTx()
defer TxDefer(tx, err)
err = txFunc()
return
}
But this is miserably incorrect because the err
it's always the original one, not the result of txFunc()
, right?
How can I fix this?
Upvotes: 1
Views: 350
Reputation:
Pass the address of the error to the function. This allows the function to access the current value of the caller's variable. It also allows function to set the variable.
Rollback and Commit return errors. These errors should be returned to the caller.
func TxDefer(tx Transaction, perr *error) {
if r := recover(); r != nil {
*perr = fmt.Errorf("panic: %v", r)
tx.Rollback()
} else if *perr != nil {
err = tx.Rollback()
if err != nil {
// replace original error with rollback error
*perr = err
}
} else {
*perr = tx.Commit()
}
}
Use it like this:
func (r *repoPG) WithTransaction(txFunc func() error) (err error) {
tx := db.NewTx()
defer TxDefer(tx, &err)
err = txFunc()
return
}
In the code above, the expression *perr
evaluates to the current value of err
in WithTransaction
. The value of err
in the question is the value of err
at the time of the defer.
Upvotes: 3