dbcontext

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2022 License: BlueOak-1.0.0 Imports: 4 Imported by: 0

README

dbcontext builds.sr.ht status Go Reference

Nestable transaction-like contexts for Go database/sql database connections. Primarily made for writing integration tests - run each test inside a TxContext, and have the application code start/rollback/commit subcontexts when it would usually use transactions.

Known issues

  • Currently only tested with MySQL
    • Uses savepoints, which are available in Postgres. Shouldn't be too hard to also test Postgres.
  • API is currently unstable
  • Contexts aren't concurrency-safe - you should only have one active SavepointContext in a TxContext at any given time. The contexts should make up a stack, and there should only be one context at any given level in the stack.
    • Database transactions aren't really concurrency-safe either, so this shouldn't be a surprise

Usage

First, install the library

go get git.sr.ht/~pmc/dbcontext

Then, use it. Check out the tests for some more examples. Here's a psuedocode example:

// In your tests:
func TestMyApp(t *testing.T) {
  db, err := sql.Open("mysql", "")
  require.NoError(t, err)
  dbcontext.WithTxContext(db, func(context *dbcontext.TxContext) (*any, error) {
    httpRequest(db, "POST", "/user/{user_id}", myUserData)
    // Do whatever testing you need here
    // WithTxContext will automatically rollback the transaction if it hasn't already been rolled back or committed
    return nil, nil
  })
}

// In your application
func PostUserHandler(db dbcontext.SqlContext, req HttpRequest) (*HttpResponse, error) {
  return dbcontext.WithSubcontext(db, func(tx dbcontext.SqlContext) (*HttpResponse, error) {
    var user User
    row := tx.QueryRow("SELECT * FROM users WHERE user_id = ?", req.UserID)
    if row.Err() != nil {
      return nil, row.Err()
    }
    err := row.Scan(&user)
    if err != nil {
      return nil, err
    }
    return &HttpResponse{
      User: user,
    }, nil
  })
}

Blue Oak Model License

Version 1.0.0

Purpose

This license gives everyone as much permission to work with this software as possible, while protecting contributors from liability.

Acceptance

In order to receive this license, you must agree to its rules. The rules of this license are both obligations under that agreement and conditions to your license. You must not do anything with this software that triggers a rule that you cannot or will not follow.

Each contributor licenses you to do everything with this software that would otherwise infringe that contributor's copyright in it.

Notices

You must ensure that everyone who gets a copy of any part of this software from you, with or without changes, also gets the text of this license or a link to https://blueoakcouncil.org/license/1.0.0.

Excuse

If anyone notifies you in writing that you have not complied with Notices, you can keep your license by taking all practical steps to comply within 30 days after the notice. If you do not do so, your license ends immediately.

Patent

Each contributor licenses you to do everything with this software that would otherwise infringe any patent claims they can license or become able to license.

Reliability

No contributor can revoke this license.

No Liability

As far as the law allows, this software comes as is, without any warranty or condition, and no contributor will be liable to anyone for any damages related to this software or this license, under any kind of legal claim.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrOperatingOnCompletedContext = fmt.Errorf("tried to operate on completed context")

Functions

func MaybeRollbackContext

func MaybeRollbackContext(context SqlContext) error

Rollback a context if it hasn't already been completed (rolled back or committed)

func WithSavepointContext

func WithSavepointContext[T *any](parent SqlContext, f func(*SavepointContext) (T, error)) (T, error)

WithSavepointContext runs a function with a new SavepointContext created as a child of the given context. If the function doesn't commit or rollback the new subcontext, it will be automatically rolled back. You should probably use WithSubcontext instead.

func WithSubcontext

func WithSubcontext[T *any](context SqlContext, f func(SqlContext) (T, error)) (T, error)

WithSubcontext runs a function with a subcontext of the given context. If the function doesn't commit or rollback the subcontext, it will be automatically rolled back.

func WithTxContext

func WithTxContext[T *any](db *sql.DB, f func(*TxContext) (T, error)) (T, error)

WithTxContext runs a function with a new TxContext created by wrapping a sql.Tx database transaction. If the function doesn't commit or rollback the transaction, it will be automatically rolled back.

Types

type SavepointContext

type SavepointContext struct {
	// contains filtered or unexported fields
}

func NewSavepointContext

func NewSavepointContext(parent SqlContext) (*SavepointContext, error)

NewSavepointContext creates a new SavepointContext as a child of another context. You should probably use [Context.Subcontext] instead.

func (*SavepointContext) Commit

func (c *SavepointContext) Commit() error

func (*SavepointContext) Completed

func (c *SavepointContext) Completed() bool

func (*SavepointContext) Ctx

func (c *SavepointContext) Ctx() context.Context

func (*SavepointContext) Exec

func (c *SavepointContext) Exec(query string, args ...any) (sql.Result, error)

func (*SavepointContext) Parent

func (c *SavepointContext) Parent() SqlContext

func (*SavepointContext) Prepare

func (c *SavepointContext) Prepare(query string) (*sql.Stmt, error)

func (*SavepointContext) Query

func (c *SavepointContext) Query(query string, args ...any) (*sql.Rows, error)

func (*SavepointContext) QueryRow

func (c *SavepointContext) QueryRow(query string, args ...any) (*sql.Row, error)

func (*SavepointContext) Rollback

func (c *SavepointContext) Rollback() error

func (*SavepointContext) Stmt

func (c *SavepointContext) Stmt(stmt *sql.Stmt) (*sql.Stmt, error)

func (*SavepointContext) Subcontext

func (c *SavepointContext) Subcontext() (SqlContext, error)

func (*SavepointContext) Tx

func (c *SavepointContext) Tx() *sql.Tx

type SqlContext

type SqlContext interface {
	// Ctx returns the [context.Context] attached to this SqlContext
	Ctx() context.Context
	// Tx returns the [sql.Tx] attached to this SqlContext. This Tx object might
	// be fetched through many layers of contexts, so be careful rolling it back
	// or committing it.
	Tx() *sql.Tx
	// Completed returns hether this context has been rolled back or committed
	Completed() bool

	// Exec executes a query that doesn't return rows. See [database/sql]
	// documentation for more details.
	Exec(query string, args ...any) (sql.Result, error)
	// Prepare creates a prepared statement for use within a transaction. See
	// [database/sql] documentation for more details.
	Prepare(query string) (*sql.Stmt, error)
	// Query executes a query that returns rows. See [database/sql] documentation
	// for more details.
	Query(query string, args ...any) (*sql.Rows, error)
	// QueryRow executes a query that is expected to return at most one row. See
	// [database/sql] documentation for more details.
	QueryRow(query string, args ...any) (*sql.Row, error)
	// Rollback executes this context's rollback behavior, reverting any INSERTs
	// and UPDATEs made. This may abort a database transaction, or perform some
	// other behavior.
	Rollback() error
	// Commit executes this context's commit behavior, making any INSERTs and
	// UPDATEs visible to higher up contexts. This may commit a database
	// transaction, perform some other behavior, or do nothing at all.
	Commit() error
	// Stmt returns a transaction-specific prepared statement from an existing
	// statement. See [database/sql] documentation for more details.
	Stmt(stmt *sql.Stmt) (*sql.Stmt, error)

	// Subcontext returns a context below this context. Complete this new
	// subcontext (by using [Commit] or [Rollback]) before performing any more
	// operations on this context, including from other goroutines.
	Subcontext() (SqlContext, error)
}

type TxContext

type TxContext struct {
	// contains filtered or unexported fields
}

func BeginTx

func BeginTx(db *sql.DB) (*TxContext, error)

BeginTx creates a new database transaction and returns a TxContext corresponding to this new transaction.

func NewContextFromTx

func NewContextFromTx(tx *sql.Tx) *TxContext

NewContextFromTx creates a TxContext wrapping a sql.Tx database transaction.

func NewContextFromTxAndCtx

func NewContextFromTxAndCtx(tx *sql.Tx, ctx context.Context) *TxContext

NewContextFromTxAndCtx creates a TxContext wrapping a sql.Tx database transaction with a context.Context which can be used to interrupt queries and statements.

func (*TxContext) Commit

func (c *TxContext) Commit() error

func (*TxContext) Completed

func (c *TxContext) Completed() bool

func (*TxContext) Ctx

func (c *TxContext) Ctx() context.Context

func (*TxContext) Exec

func (c *TxContext) Exec(query string, args ...any) (sql.Result, error)

func (*TxContext) Prepare

func (c *TxContext) Prepare(query string) (*sql.Stmt, error)

func (*TxContext) Query

func (c *TxContext) Query(query string, args ...any) (*sql.Rows, error)

func (*TxContext) QueryRow

func (c *TxContext) QueryRow(query string, args ...any) (*sql.Row, error)

func (*TxContext) Rollback

func (c *TxContext) Rollback() error

func (*TxContext) Stmt

func (c *TxContext) Stmt(stmt *sql.Stmt) (*sql.Stmt, error)

func (*TxContext) Subcontext

func (c *TxContext) Subcontext() (SqlContext, error)

func (*TxContext) Tx

func (c *TxContext) Tx() *sql.Tx

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL