sqlhooks

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Nov 25, 2019 License: MIT Imports: 4 Imported by: 30

README

sqlhooks Build Status Coverage Status Go Report Card

Attach hooks to any database/sql driver.

The purpose of sqlhooks is to provide a way to instrument your sql statements, making really easy to log queries or measure execution time without modifying your actual code.

Install

go get github.com/gchaincl/sqlhooks

Requires Go >= 1.8.x

Breaking changes

V1 isn't backward compatible with previous versions, if you want to fetch old versions, you can get them from gopkg.in

go get gopkg.in/gchaincl/sqlhooks.v0

Usage GoDoc

// This example shows how to instrument sql queries in order to display the time that they consume
package main

import (
	"context"
	"database/sql"
	"fmt"
	"time"

	"github.com/gchaincl/sqlhooks"
	"github.com/mattn/go-sqlite3"
)

// Hooks satisfies the sqlhook.Hooks interface
type Hooks struct {}

// Before hook will print the query with it's args and return the context with the timestamp
func (h *Hooks) Before(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
	fmt.Printf("> %s %q", query, args)
	return context.WithValue(ctx, "begin", time.Now()), nil
}

// After hook will get the timestamp registered on the Before hook and print the elapsed time
func (h *Hooks) After(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
	begin := ctx.Value("begin").(time.Time)
	fmt.Printf(". took: %s\n", time.Since(begin))
	return ctx, nil
}

func main() {
	// First, register the wrapper
	sql.Register("sqlite3WithHooks", sqlhooks.Wrap(&sqlite3.SQLiteDriver{}, &Hooks{}))

	// Connect to the registered wrapped driver
	db, _ := sql.Open("sqlite3WithHooks", ":memory:")

	// Do you're stuff
	db.Exec("CREATE TABLE t (id INTEGER, text VARCHAR(16))")
	db.Exec("INSERT into t (text) VALUES(?), (?)", "foo", "bar")
	db.Query("SELECT id, text FROM t")
}

/*
Output should look like:
> CREATE TABLE t (id INTEGER, text VARCHAR(16)) []. took: 121.238µs
> INSERT into t (text) VALUES(?), (?) ["foo" "bar"]. took: 36.364µs
> SELECT id, text FROM t []. took: 4.653µs
*/

Benchmarks

 go test -bench=. -benchmem
 BenchmarkSQLite3/Without_Hooks-4                  200000              8572 ns/op             627 B/op         16 allocs/op
 BenchmarkSQLite3/With_Hooks-4                     200000             10231 ns/op             738 B/op         18 allocs/op
 BenchmarkMySQL/Without_Hooks-4                     10000            108421 ns/op             437 B/op         10 allocs/op
 BenchmarkMySQL/With_Hooks-4                        10000            226085 ns/op             597 B/op         13 allocs/op
 BenchmarkPostgres/Without_Hooks-4                  10000            125718 ns/op             649 B/op         17 allocs/op
 BenchmarkPostgres/With_Hooks-4                      5000            354831 ns/op            1122 B/op         27 allocs/op
 PASS
 ok      github.com/gchaincl/sqlhooks    11.713s

Documentation

Overview

This example shows how to instrument sql queries in order to display the time that they consume package main

import (

"context"
"database/sql"
"fmt"
"time"

"github.com/gchaincl/sqlhooks"
"github.com/mattn/go-sqlite3"

)

// Hooks satisfies the sqlhook.Hooks interface type Hooks struct {}

// Before hook will print the query with it's args and return the context with the timestamp

func (h *Hooks) Before(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
	fmt.Printf("> %s %q", query, args)
	return context.WithValue(ctx, "begin", time.Now()), nil
}

// After hook will get the timestamp registered on the Before hook and print the elapsed time

func (h *Hooks) After(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
	begin := ctx.Value("begin").(time.Time)
	fmt.Printf(". took: %s\n", time.Since(begin))
	return ctx, nil
}
func main() {
	// First, register the wrapper
	sql.Register("sqlite3WithHooks", sqlhooks.Wrap(&sqlite3.SQLiteDriver{}, &Hooks{}))

	// Connect to the registered wrapped driver
	db, _ := sql.Open("sqlite3WithHooks", ":memory:")

	// Do you're stuff
	db.Exec("CREATE TABLE t (id INTEGER, text VARCHAR(16))")
	db.Exec("INSERT into t (text) VALUES(?), (?)", "foo", "bar")
	db.Query("SELECT id, text FROM t")
}

/* Output should look like: > CREATE TABLE t (id INTEGER, text VARCHAR(16)) []. took: 121.238µs > INSERT into t (text) VALUES(?), (?) ["foo" "bar"]. took: 36.364µs > SELECT id, text FROM t []. took: 4.653µs */

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Wrap

func Wrap(driver driver.Driver, hooks Hooks) driver.Driver

Wrap is used to create a new instrumented driver, it takes a vendor specific driver, and a Hooks instance to produce a new driver instance. It's usually used inside a sql.Register() statement

Types

type Conn

type Conn struct {
	Conn driver.Conn
	// contains filtered or unexported fields
}

Conn implements a database/sql.driver.Conn

func (*Conn) Begin

func (conn *Conn) Begin() (driver.Tx, error)

func (*Conn) Close

func (conn *Conn) Close() error

func (*Conn) Prepare

func (conn *Conn) Prepare(query string) (driver.Stmt, error)

func (*Conn) PrepareContext

func (conn *Conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error)

type Driver

type Driver struct {
	driver.Driver
	// contains filtered or unexported fields
}

Driver implements a database/sql/driver.Driver

func (*Driver) Open

func (drv *Driver) Open(name string) (driver.Conn, error)

Open opens a connection

type ErrorHook added in v1.1.0

type ErrorHook func(ctx context.Context, err error, query string, args ...interface{}) error

ErrorHook is the error handling callback signature

type ExecerContext added in v1.1.0

type ExecerContext struct {
	*Conn
}

ExecerContext implements a database/sql.driver.ExecerContext

func (*ExecerContext) Exec added in v1.1.0

func (conn *ExecerContext) Exec(query string, args []driver.Value) (driver.Result, error)

func (*ExecerContext) ExecContext added in v1.1.0

func (conn *ExecerContext) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error)

type ExecerQueryerContext added in v1.2.0

type ExecerQueryerContext struct {
	*Conn
	*ExecerContext
	*QueryerContext
}

ExecerQueryerContext implements database/sql.driver.ExecerContext and database/sql.driver.QueryerContext

type ExecerQueryerContextWithSessionResetter added in v1.2.0

type ExecerQueryerContextWithSessionResetter struct {
	*Conn
	*ExecerContext
	*QueryerContext
	*SessionResetter
}

ExecerQueryerContext implements database/sql.driver.ExecerContext and database/sql.driver.QueryerContext

type Hook

type Hook func(ctx context.Context, query string, args ...interface{}) (context.Context, error)

Hook is the hook callback signature

type Hooks

type Hooks interface {
	Before(ctx context.Context, query string, args ...interface{}) (context.Context, error)
	After(ctx context.Context, query string, args ...interface{}) (context.Context, error)
}

Hooks instances may be passed to Wrap() to define an instrumented driver

func Compose added in v1.3.0

func Compose(hooks ...Hooks) Hooks

Compose allows for composing multiple Hooks into one. It runs every callback on every hook in argument order, even if previous hooks return an error. If multiple hooks return errors, the error return value will be MultipleErrors, which allows for introspecting the errors if necessary.

type MultipleErrors added in v1.3.0

type MultipleErrors []error

MultipleErrors is an error that contains multiple errors.

func (MultipleErrors) As added in v1.3.0

func (m MultipleErrors) As(target interface{}) bool

Is tries to convert each wrapped error to target with errors.As() and returns true that succeeds. If none of the errors are convertible, returns false.

func (MultipleErrors) Error added in v1.3.0

func (m MultipleErrors) Error() string

func (MultipleErrors) Is added in v1.3.0

func (m MultipleErrors) Is(target error) bool

Is returns true if any of the wrapped errors is target according to errors.Is()

type OnErrorer added in v1.1.0

type OnErrorer interface {
	OnError(ctx context.Context, err error, query string, args ...interface{}) error
}

OnErrorer instances will be called if any error happens

type QueryerContext added in v1.2.0

type QueryerContext struct {
	*Conn
}

QueryerContext implements a database/sql.driver.QueryerContext

func (*QueryerContext) QueryContext added in v1.2.0

func (conn *QueryerContext) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error)

type SessionResetter added in v1.2.0

type SessionResetter struct {
	*Conn
}

func (*SessionResetter) ResetSession added in v1.2.0

func (s *SessionResetter) ResetSession(ctx context.Context) error

type Stmt

type Stmt struct {
	Stmt driver.Stmt
	// contains filtered or unexported fields
}

Stmt implements a database/sql/driver.Stmt

func (*Stmt) Close

func (stmt *Stmt) Close() error

func (*Stmt) Exec

func (stmt *Stmt) Exec(args []driver.Value) (driver.Result, error)

func (*Stmt) ExecContext

func (stmt *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error)

func (*Stmt) NumInput

func (stmt *Stmt) NumInput() int

func (*Stmt) Query

func (stmt *Stmt) Query(args []driver.Value) (driver.Rows, error)

func (*Stmt) QueryContext

func (stmt *Stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error)

Directories

Path Synopsis
hooks

Jump to

Keyboard shortcuts

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