sqlxtransaction

sqlxtransaction handles begin/commit/rollback control flow around business logic executed inside a sqlx transaction.

Part of nurago, a collection of independent Go packages for backend services.

import "github.com/tecnickcom/nurago/pkg/sqlxtransaction"

Package sqlxtransaction handles begin/commit/rollback control flow around business logic executed inside a sqlx transaction.

How It Works

Exec and ExecWithOptions accept a caller-provided ExecFunc and run it inside a sqlx transaction:

  1. Start a transaction with DB.BeginTxx.
  2. Execute the provided function with the transaction object.
  3. Commit if execution succeeds.
  4. Roll back automatically if execution fails or commit is not reached.

Rollback behavior is deferred and guarded:

  • rollback is skipped after a successful commit,
  • sql.ErrTxDone is ignored during deferred rollback,
  • rollback failures are joined with the current error so diagnostics are not lost.

Usage

err := sqlxtransaction.Exec(ctx, db, func(ctx context.Context, tx *sqlx.Tx) error {
    // Execute all related SQL operations using tx.
    // Return an error to trigger rollback.
    return nil
})
if err != nil {
    return err
}

For a similar helper based on the standard database/sql package (instead of github.com/jmoiron/sqlx), see: github.com/tecnickcom/nurago/pkg/sqltransaction

When To Use

  • You use sqlx and want struct scanning inside a transaction.
  • Rollback must be guaranteed on error and on panic.

Example

// A real program would use a *sqlx.DB from sqlx.Connect. The mock keeps
// the example self-contained and its output deterministic.
mockDB, mock, err := sqlmock.New()
if err != nil {
	fmt.Println(err)

	return
}

db := sqlx.NewDb(mockDB, "sqlmock")

defer func() { _ = db.Close() }()

mock.ExpectBegin()
mock.ExpectExec("INSERT INTO audit").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()

// The transaction is committed when run returns nil, and rolled back
// when it returns an error or panics.
err = sqlxtransaction.Exec(
	context.TODO(),
	db,
	func(ctx context.Context, tx *sqlx.Tx) error {
		_, err := tx.ExecContext(ctx, "INSERT INTO audit (event) VALUES ('login')")
		if err != nil {
			return fmt.Errorf("writing audit row: %w", err)
		}

		return nil
	},
)

fmt.Println("committed:", err)

// Output:
// committed: <nil>

Full source is in example_sqlxtransaction_test.go. More runnable examples are on pkg.go.dev.

Dependencies

Importing this package pulls 1 external module:

  • github.com/jmoiron/sqlx