database/sql: do not store Tx options in Context

Drivers which previously supported tip will need to update to this
revision before release.

Fixes #18284

Change-Id: I70b8e7afff1558a8b5348885ce9f50e067c72ee9
Reviewed-on: https://go-review.googlesource.com/34330
Run-TryBot: Daniel Theophanes <kardianos@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Daniel Theophanes 2016-12-13 07:55:12 -08:00 committed by Brad Fitzpatrick
parent fe07091f9e
commit d0501f1da9
6 changed files with 65 additions and 84 deletions

View file

@ -18,7 +18,6 @@ package sql
import (
"context"
"database/sql/driver"
"database/sql/internal"
"errors"
"fmt"
"io"
@ -115,12 +114,10 @@ func Named(name string, value interface{}) NamedArg {
return NamedArg{Name: name, Value: value}
}
// IsolationLevel is the transaction isolation level stored in Context.
// The IsolationLevel is set with IsolationContext and the context
// should be passed to BeginContext.
// IsolationLevel is the transaction isolation level used in TxOptions.
type IsolationLevel int
// Various isolation levels that drivers may support in BeginContext.
// Various isolation levels that drivers may support in BeginTx.
// If a driver does not support a given isolation level an error may be returned.
//
// See https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels.
@ -135,18 +132,12 @@ const (
LevelLinearizable
)
// IsolationContext returns a new Context that carries the provided isolation level.
// The context must contain the isolation level before beginning the transaction
// with BeginContext.
func IsolationContext(ctx context.Context, level IsolationLevel) context.Context {
return context.WithValue(ctx, internal.IsolationLevelKey{}, driver.IsolationLevel(level))
}
// ReadOnlyWithContext returns a new Context that carries the provided
// read-only transaction property. The context must contain the read-only property
// before beginning the transaction with BeginContext.
func ReadOnlyContext(ctx context.Context) context.Context {
return context.WithValue(ctx, internal.ReadOnlyKey{}, true)
// TxOptions holds the transaction options to be used in DB.BeginTx.
type TxOptions struct {
// Isolation is the transaction isolation level.
// If zero, the driver or database's default level is used.
Isolation IsolationLevel
ReadOnly bool
}
// RawBytes is a byte slice that holds a reference to memory owned by
@ -1311,28 +1302,27 @@ func (db *DB) QueryRow(query string, args ...interface{}) *Row {
return db.QueryRowContext(context.Background(), query, args...)
}
// BeginContext starts a transaction.
// BeginTx starts a transaction.
//
// The provided context is used until the transaction is committed or rolled back.
// If the context is canceled, the sql package will roll back
// the transaction. Tx.Commit will return an error if the context provided to
// BeginContext is canceled.
// BeginTx is canceled.
//
// An isolation level may be set by setting the value in the context
// before calling this. If a non-default isolation level is used
// that the driver doesn't support an error will be returned. Different drivers
// may have slightly different meanings for the same isolation level.
func (db *DB) BeginContext(ctx context.Context) (*Tx, error) {
// The provided TxOptions is optional and may be nil if defaults should be used.
// If a non-default isolation level is used that the driver doesn't support,
// an error will be returned.
func (db *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error) {
var tx *Tx
var err error
for i := 0; i < maxBadConnRetries; i++ {
tx, err = db.begin(ctx, cachedOrNewConn)
tx, err = db.begin(ctx, opts, cachedOrNewConn)
if err != driver.ErrBadConn {
break
}
}
if err == driver.ErrBadConn {
return db.begin(ctx, alwaysNewConn)
return db.begin(ctx, opts, alwaysNewConn)
}
return tx, err
}
@ -1340,17 +1330,17 @@ func (db *DB) BeginContext(ctx context.Context) (*Tx, error) {
// Begin starts a transaction. The default isolation level is dependent on
// the driver.
func (db *DB) Begin() (*Tx, error) {
return db.BeginContext(context.Background())
return db.BeginTx(context.Background(), nil)
}
func (db *DB) begin(ctx context.Context, strategy connReuseStrategy) (tx *Tx, err error) {
func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStrategy) (tx *Tx, err error) {
dc, err := db.conn(ctx, strategy)
if err != nil {
return nil, err
}
var txi driver.Tx
withLock(dc, func() {
txi, err = ctxDriverBegin(ctx, dc.ci)
txi, err = ctxDriverBegin(ctx, opts, dc.ci)
})
if err != nil {
db.putConn(dc, err)