Documentation ¶
Overview ¶
Package dbwrap provides sql.DB wrapper to call custom code around operations.
Index ¶
- Constants
- func Caller(skipPackages ...string) string
- func CallerCtx(ctx context.Context, skipPackages ...string) string
- func Register(driverName string, options ...Option) (string, error)
- func RegisterWithSource(driverName string, source string, options ...Option) (string, error)
- func WithCaller(ctx context.Context, caller string) context.Context
- func Wrap(d driver.Driver, options ...Option) driver.Driver
- func WrapConn(c driver.Conn, options ...Option) driver.Conn
- func WrapConnector(dc driver.Connector, options ...Option) driver.Connector
- type Middleware
- type Operation
- type Option
- type Options
Examples ¶
Constants ¶
const ( Ping = Operation("ping") Exec = Operation("exec") Query = Operation("query") Prepare = Operation("prepare") Begin = Operation("begin") LastInsertID = Operation("last_insert_id") RowsAffected = Operation("rows_affected") StmtExec = Operation("stmt_exec") StmtQuery = Operation("stmt_query") StmtClose = Operation("stmt_close") RowsClose = Operation("rows_close") RowsNext = Operation("rows_next") Commit = Operation("commit") Rollback = Operation("rollback") )
These constants enumerate available SQL operations.
Variables ¶
This section is empty.
Functions ¶
func Caller ¶
Caller returns name and package of closest parent function that does not belong to skipped packages.
For example the result could be
pressly/goose.MySQLDialect.dbVersionQuery
func CallerCtx ¶ added in v0.1.4
CallerCtx checks context for a pre-defined caller value or returns caller from runtime stack.
func Register ¶
Register initializes and registers our wrapped database driver identified by its driverName and using provided Options. On success it returns the generated driverName to use when calling sql.Open. It is possible to register multiple wrappers for the same database driver if needing different Options for different connections.
func RegisterWithSource ¶
RegisterWithSource initializes and registers our wrapped database driver identified by its driverName, using provided Options. source is useful if some drivers do not accept the empty string when opening the DB. On success it returns the generated driverName to use when calling sql.Open. It is possible to register multiple wrappers for the same database driver if needing different Options for different connections.
func WithCaller ¶ added in v0.1.4
WithCaller overrides context with pre-defined caller value.
func WrapConnector ¶
WrapConnector allows wrapping a database driver.Connector which eliminates the need to register wrap as an available driver.Driver.
Example ¶
package main import ( "context" "database/sql" "database/sql/driver" "log" "time" "github.com/bool64/dbwrap" ) func main() { // Initialize dbConnector with your SQL Driver, for example for MySQL: // dbConnector, err = /*github.com/go-sql-driver*/mysql.NewConnector(myCfg) // or for PostgreSQL: // dbConnector, err = /*github.com/lib*/pq.NewConnector("postgres://user:pass@localhost/db") var dbConnector driver.Connector // Wrap connector to enable statement logging. dbConnector = dbwrap.WrapConnector(dbConnector, // This interceptor improves observability on DB side. dbwrap.WithInterceptor(func(ctx context.Context, operation dbwrap.Operation, statement string, args []driver.NamedValue) (context.Context, string, []driver.NamedValue) { // Closest caller in the stack with package not equal to listed and to "database/sql". // Put your database helper packages here, so that caller bubbles up to app level. caller := dbwrap.CallerCtx(ctx, "github.com/Masterminds/squirrel", "github.com/jmoiron/sqlx", ) // Add caller name as statement comment. // Example instrumented statement: // SELECT version_id, is_applied from schema_migrations ORDER BY id DESC -- pressly/goose.MySQLDialect.dbVersionQuery return ctx, statement + " -- " + caller, args }), // This option limits middleware applicability. dbwrap.WithOperations(dbwrap.Query, dbwrap.StmtQuery, dbwrap.Exec, dbwrap.StmtExec), // This middleware logs statements with arguments. dbwrap.WithMiddleware( func( ctx context.Context, operation dbwrap.Operation, statement string, args []driver.NamedValue, ) (nCtx context.Context, onFinish func(error)) { // Closest caller in the stack with package not equal to listed and to "database/sql". caller := dbwrap.CallerCtx(ctx, "github.com/Masterminds/squirrel", "github.com/jmoiron/sqlx", ) started := time.Now() return ctx, func(err error) { res := " complete" if err != nil { res = " failed" } log.Println( caller+" "+string(operation)+res, statement, time.Since(started).String(), args, err, ) } }), ) // Open database with instrumented connector. db := sql.OpenDB(dbConnector) // Use database. _, err := db.Query("SELECT * FROM table") if err != nil { log.Fatal(err) } }
Output:
Types ¶
type Middleware ¶
type Middleware func( ctx context.Context, operation Operation, statement string, args []driver.NamedValue, ) (nCtx context.Context, onFinish func(error))
Middleware returns instrumented context and finalizer callback.
Middleware is invoked before operation. Returned onFinish function is invoked after the operation.
type Option ¶
type Option func(o *Options)
Option allows for managing wrapper configuration using functional options.
func WithAllOperations ¶
func WithAllOperations() Option
WithAllOperations enables all operations to be wrapped with middleware. This also includes RowsNext.
func WithInterceptor ¶
func WithInterceptor(i func( ctx context.Context, operation Operation, statement string, args []driver.NamedValue, ) (context.Context, string, []driver.NamedValue), ) Option
WithInterceptor sets statement interceptor to a db wrapper. Interceptor receives every statement that is to be requested and can change it.
func WithMiddleware ¶
func WithMiddleware(mw ...Middleware) Option
WithMiddleware adds one or multiple middlewares to a db wrapper.
func WithOperations ¶
WithOperations controls which operations should be wrapped with middlewares. It does not affect statement interceptor.
func WithOptions ¶
WithOptions sets our wrapper options through a single Options object.
type Options ¶
type Options struct { // Middlewares wrap operations. Middlewares []Middleware // Intercept mutates statement and/or parameters. Intercept func( ctx context.Context, operation Operation, statement string, args []driver.NamedValue, ) (context.Context, string, []driver.NamedValue) // Operations lists which operations should be wrapped. Operations []Operation // contains filtered or unexported fields }
Options holds configuration of our wrapper. By default all options are set to false intentionally when creating a wrapped driver and provide the most sensible default with both performance and security in mind.