/sqalx

Nested transactions for sqlx

Primary LanguageGoMIT LicenseMIT

⚠️ Warning: This repository is considered inactive and no change will be made to it except for security updates.

sqalx

GoDoc Go Report Card

sqalx (pronounced 'scale-x') is a library built on top of sqlx that allows to seamlessly create nested transactions and to avoid thinking about whether or not a function is called within a transaction. With sqalx you can easily create reusable and composable functions that can be called within or out of transactions and that can create transactions themselves.

Getting started

$ go get github.com/heetch/sqalx

Import sqalx

import "github.com/heetch/sqalx"

Usage

package main

import (
	"log"

	"github.com/heetch/sqalx"
	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"
)

func main() {
	// Connect to PostgreSQL with sqlx.
	db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
	if err != nil {
		log.Fatal(err)
	}

	defer db.Close()

	// Pass the db to sqalx.
	// It returns a sqalx.Node. A Node is a wrapper around sqlx.DB or sqlx.Tx.
	node, err := sqalx.New(db)
	if err != nil {
		log.Fatal(err)
	}

	err = createUser(node)
	if err != nil {
		log.Fatal(err)
	}
}

func createUser(node sqalx.Node) error {
	// Exec a query
	_, _ = node.Exec("INSERT INTO ....") // you can use a node as if it were a *sqlx.DB or a *sqlx.Tx

	// Let's create a transaction.
	// A transaction is also a sqalx.Node.
	tx, err := node.Beginx()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	_, _ = tx.Exec("UPDATE ...")

	// Now we call another function and pass it the transaction.
	err = updateGroups(tx)
	if err != nil {
		return nil
	}

	return tx.Commit()
}

func updateGroups(node sqalx.Node) error {
	// Notice we are creating a new transaction.
	// This would normally cause a dead lock without sqalx.
	tx, err := node.Beginx()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	_, _ = tx.Exec("INSERT ...")
	_, _ = tx.Exec("UPDATE ...")
	_, _ = tx.Exec("DELETE ...")

	return tx.Commit()
}

PostgreSQL Savepoints

When using the PostgreSQL driver, an option can be passed to New to enable the use of PostgreSQL Savepoints for nested transactions.

node, err := sqalx.New(db, sqalx.SavePoint(true))

Issue

Please open an issue if you encounter any problem.

Development

sqalx is covered by a go test suite. In order to test against specific databases we include a docker-compose file that runs Postgres and MySQL.

Running all tests

To run the tests, first run docker-compose up to run both Postgres and MySQL in locally-exposed docker images. Then run your tests via make test which sets up the above described data sources and runs all tests.

Running specific tests

To test against the Postgres instance be sure to export the following DSN:

export POSTGRESQL_DATASOURCE="postgresql://sqalx:sqalx@localhost:5432/sqalx?sslmode=disable"

To test against the MySQL instance be sure to export the following DSN:

export MYSQL_DATASOURCE="sqalx:sqalx@tcp(localhost:3306)/sqalx"

To test against SQlite export the following DSN:

export SQLITE_DATASOURCE=":memory:"

Note: If you are developing on an M1 Mac you will need to use the officially supported by Oracle image rather than the default mysql:tag image. It is commented out in docker-compose.yml.

License

The library is released under the MIT license. See LICENSE file.