mirror of
https://github.com/golang/go
synced 2024-11-22 20:14:40 -07:00
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:
parent
fe07091f9e
commit
d0501f1da9
@ -73,10 +73,8 @@ pkg database/sql, const LevelSnapshot = 5
|
|||||||
pkg database/sql, const LevelSnapshot IsolationLevel
|
pkg database/sql, const LevelSnapshot IsolationLevel
|
||||||
pkg database/sql, const LevelWriteCommitted = 3
|
pkg database/sql, const LevelWriteCommitted = 3
|
||||||
pkg database/sql, const LevelWriteCommitted IsolationLevel
|
pkg database/sql, const LevelWriteCommitted IsolationLevel
|
||||||
pkg database/sql/driver, func IsolationFromContext(context.Context) (IsolationLevel, bool)
|
pkg database/sql/driver, type ConnBeginTx interface { BeginTx }
|
||||||
pkg database/sql/driver, func ReadOnlyFromContext(context.Context) bool
|
pkg database/sql/driver, type ConnBeginTx interface, BeginTx(context.Context, TxOptions) (Tx, error)
|
||||||
pkg database/sql/driver, type ConnBeginContext interface { BeginContext }
|
|
||||||
pkg database/sql/driver, type ConnBeginContext interface, BeginContext(context.Context) (Tx, error)
|
|
||||||
pkg database/sql/driver, type ConnPrepareContext interface { PrepareContext }
|
pkg database/sql/driver, type ConnPrepareContext interface { PrepareContext }
|
||||||
pkg database/sql/driver, type ConnPrepareContext interface, PrepareContext(context.Context, string) (Stmt, error)
|
pkg database/sql/driver, type ConnPrepareContext interface, PrepareContext(context.Context, string) (Stmt, error)
|
||||||
pkg database/sql/driver, type ExecerContext interface { ExecContext }
|
pkg database/sql/driver, type ExecerContext interface { ExecContext }
|
||||||
@ -125,16 +123,17 @@ pkg database/sql/driver, type StmtExecContext interface { ExecContext }
|
|||||||
pkg database/sql/driver, type StmtExecContext interface, ExecContext(context.Context, []NamedValue) (Result, error)
|
pkg database/sql/driver, type StmtExecContext interface, ExecContext(context.Context, []NamedValue) (Result, error)
|
||||||
pkg database/sql/driver, type StmtQueryContext interface { QueryContext }
|
pkg database/sql/driver, type StmtQueryContext interface { QueryContext }
|
||||||
pkg database/sql/driver, type StmtQueryContext interface, QueryContext(context.Context, []NamedValue) (Rows, error)
|
pkg database/sql/driver, type StmtQueryContext interface, QueryContext(context.Context, []NamedValue) (Rows, error)
|
||||||
pkg database/sql, func IsolationContext(context.Context, IsolationLevel) context.Context
|
pkg database/sql/driver, type TxOptions struct
|
||||||
|
pkg database/sql/driver, type TxOptions struct, Isolation IsolationLevel
|
||||||
|
pkg database/sql/driver, type TxOptions struct, ReadOnly bool
|
||||||
pkg database/sql, func Named(string, interface{}) NamedArg
|
pkg database/sql, func Named(string, interface{}) NamedArg
|
||||||
pkg database/sql, func ReadOnlyContext(context.Context) context.Context
|
|
||||||
pkg database/sql, method (*ColumnType) DatabaseTypeName() string
|
pkg database/sql, method (*ColumnType) DatabaseTypeName() string
|
||||||
pkg database/sql, method (*ColumnType) DecimalSize() (int64, int64, bool)
|
pkg database/sql, method (*ColumnType) DecimalSize() (int64, int64, bool)
|
||||||
pkg database/sql, method (*ColumnType) Length() (int64, bool)
|
pkg database/sql, method (*ColumnType) Length() (int64, bool)
|
||||||
pkg database/sql, method (*ColumnType) Name() string
|
pkg database/sql, method (*ColumnType) Name() string
|
||||||
pkg database/sql, method (*ColumnType) Nullable() (bool, bool)
|
pkg database/sql, method (*ColumnType) Nullable() (bool, bool)
|
||||||
pkg database/sql, method (*ColumnType) ScanType() reflect.Type
|
pkg database/sql, method (*ColumnType) ScanType() reflect.Type
|
||||||
pkg database/sql, method (*DB) BeginContext(context.Context) (*Tx, error)
|
pkg database/sql, method (*DB) BeginTx(context.Context, *TxOptions) (*Tx, error)
|
||||||
pkg database/sql, method (*DB) ExecContext(context.Context, string, ...interface{}) (Result, error)
|
pkg database/sql, method (*DB) ExecContext(context.Context, string, ...interface{}) (Result, error)
|
||||||
pkg database/sql, method (*DB) PingContext(context.Context) error
|
pkg database/sql, method (*DB) PingContext(context.Context) error
|
||||||
pkg database/sql, method (*DB) PrepareContext(context.Context, string) (*Stmt, error)
|
pkg database/sql, method (*DB) PrepareContext(context.Context, string) (*Stmt, error)
|
||||||
@ -155,6 +154,9 @@ pkg database/sql, type IsolationLevel int
|
|||||||
pkg database/sql, type NamedArg struct
|
pkg database/sql, type NamedArg struct
|
||||||
pkg database/sql, type NamedArg struct, Name string
|
pkg database/sql, type NamedArg struct, Name string
|
||||||
pkg database/sql, type NamedArg struct, Value interface{}
|
pkg database/sql, type NamedArg struct, Value interface{}
|
||||||
|
pkg database/sql, type TxOptions struct
|
||||||
|
pkg database/sql, type TxOptions struct, Isolation IsolationLevel
|
||||||
|
pkg database/sql, type TxOptions struct, ReadOnly bool
|
||||||
pkg debug/pe, method (*COFFSymbol) FullName(StringTable) (string, error)
|
pkg debug/pe, method (*COFFSymbol) FullName(StringTable) (string, error)
|
||||||
pkg debug/pe, method (StringTable) String(uint32) (string, error)
|
pkg debug/pe, method (StringTable) String(uint32) (string, error)
|
||||||
pkg debug/pe, type File struct, COFFSymbols []COFFSymbol
|
pkg debug/pe, type File struct, COFFSymbols []COFFSymbol
|
||||||
|
@ -111,25 +111,32 @@ func ctxDriverStmtQuery(ctx context.Context, si driver.Stmt, nvdargs []driver.Na
|
|||||||
|
|
||||||
var errLevelNotSupported = errors.New("sql: selected isolation level is not supported")
|
var errLevelNotSupported = errors.New("sql: selected isolation level is not supported")
|
||||||
|
|
||||||
func ctxDriverBegin(ctx context.Context, ci driver.Conn) (driver.Tx, error) {
|
func ctxDriverBegin(ctx context.Context, opts *TxOptions, ci driver.Conn) (driver.Tx, error) {
|
||||||
if ciCtx, is := ci.(driver.ConnBeginContext); is {
|
if ciCtx, is := ci.(driver.ConnBeginTx); is {
|
||||||
return ciCtx.BeginContext(ctx)
|
dopts := driver.TxOptions{}
|
||||||
|
if opts != nil {
|
||||||
|
dopts.Isolation = driver.IsolationLevel(opts.Isolation)
|
||||||
|
dopts.ReadOnly = opts.ReadOnly
|
||||||
|
}
|
||||||
|
return ciCtx.BeginTx(ctx, dopts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Done() == context.Background().Done() {
|
if ctx.Done() == context.Background().Done() {
|
||||||
return ci.Begin()
|
return ci.Begin()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the transaction level in ctx. If set and non-default
|
if opts != nil {
|
||||||
// then return an error here as the BeginContext driver value is not supported.
|
// Check the transaction level. If the transaction level is non-default
|
||||||
if level, ok := driver.IsolationFromContext(ctx); ok && level != driver.IsolationLevel(LevelDefault) {
|
// then return an error here as the BeginTx driver value is not supported.
|
||||||
return nil, errors.New("sql: driver does not support non-default isolation level")
|
if opts.Isolation != LevelDefault {
|
||||||
}
|
return nil, errors.New("sql: driver does not support non-default isolation level")
|
||||||
|
}
|
||||||
|
|
||||||
// Check for a read-only parameter in ctx. If a read-only transaction is
|
// If a read-only transaction is requested return an error as the
|
||||||
// requested return an error as the BeginContext driver value is not supported.
|
// BeginTx driver value is not supported.
|
||||||
if ro := driver.ReadOnlyFromContext(ctx); ro {
|
if opts.ReadOnly {
|
||||||
return nil, errors.New("sql: driver does not support read-only transactions")
|
return nil, errors.New("sql: driver does not support read-only transactions")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
txi, err := ci.Begin()
|
txi, err := ci.Begin()
|
||||||
|
@ -10,7 +10,6 @@ package driver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql/internal"
|
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
@ -157,7 +156,7 @@ type Conn interface {
|
|||||||
|
|
||||||
// Begin starts and returns a new transaction.
|
// Begin starts and returns a new transaction.
|
||||||
//
|
//
|
||||||
// Deprecated: Drivers should implement ConnBeginContext instead (or additionally).
|
// Deprecated: Drivers should implement ConnBeginTx instead (or additionally).
|
||||||
Begin() (Tx, error)
|
Begin() (Tx, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,41 +168,35 @@ type ConnPrepareContext interface {
|
|||||||
PrepareContext(ctx context.Context, query string) (Stmt, error)
|
PrepareContext(ctx context.Context, query string) (Stmt, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsolationLevel is the transaction isolation level stored in Context.
|
// IsolationLevel is the transaction isolation level stored in TxOptions.
|
||||||
//
|
//
|
||||||
// This type should be considered identical to sql.IsolationLevel along
|
// This type should be considered identical to sql.IsolationLevel along
|
||||||
// with any values defined on it.
|
// with any values defined on it.
|
||||||
type IsolationLevel int
|
type IsolationLevel int
|
||||||
|
|
||||||
// IsolationFromContext extracts the isolation level from a Context.
|
// TxOptions holds the transaction options.
|
||||||
func IsolationFromContext(ctx context.Context) (level IsolationLevel, ok bool) {
|
//
|
||||||
level, ok = ctx.Value(internal.IsolationLevelKey{}).(IsolationLevel)
|
// This type should be considered identical to sql.TxOptions.
|
||||||
return level, ok
|
type TxOptions struct {
|
||||||
|
Isolation IsolationLevel
|
||||||
|
ReadOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOnlyFromContext extracts the read-only property from a Context.
|
// ConnBeginTx enhances the Conn interface with context and TxOptions.
|
||||||
// When readonly is true the transaction must be set to read-only
|
type ConnBeginTx interface {
|
||||||
// or return an error.
|
// BeginTx starts and returns a new transaction.
|
||||||
func ReadOnlyFromContext(ctx context.Context) (readonly bool) {
|
|
||||||
readonly, _ = ctx.Value(internal.ReadOnlyKey{}).(bool)
|
|
||||||
return readonly
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnBeginContext enhances the Conn interface with context.
|
|
||||||
type ConnBeginContext interface {
|
|
||||||
// BeginContext starts and returns a new transaction.
|
|
||||||
// If the context is canceled by the user the sql package will
|
// If the context is canceled by the user the sql package will
|
||||||
// call Tx.Rollback before discarding and closing the connection.
|
// call Tx.Rollback before discarding and closing the connection.
|
||||||
//
|
//
|
||||||
// This must call IsolationFromContext to determine if there is a set
|
// This must check opts.Isolation to determine if there is a set
|
||||||
// isolation level. If the driver does not support setting the isolation
|
// isolation level. If the driver does not support a non-default
|
||||||
// level and one is set or if there is a set isolation level
|
// level and one is set or if there is a non-default isolation level
|
||||||
// but the set level is not supported, an error must be returned.
|
// that is not supported, an error must be returned.
|
||||||
//
|
//
|
||||||
// This must also call ReadOnlyFromContext to determine if the read-only
|
// This must also check opts.ReadOnly to determine if the read-only
|
||||||
// value is true to either set the read-only transaction property if supported
|
// value is true to either set the read-only transaction property if supported
|
||||||
// or return an error if it is not supported.
|
// or return an error if it is not supported.
|
||||||
BeginContext(ctx context.Context) (Tx, error)
|
BeginTx(ctx context.Context, opts TxOptions) (Tx, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result is the result of a query execution.
|
// Result is the result of a query execution.
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package internal
|
|
||||||
|
|
||||||
// Context keys that set transaction properties for sql.BeginContext.
|
|
||||||
type (
|
|
||||||
IsolationLevelKey struct{} // context value is driver.IsolationLevel
|
|
||||||
ReadOnlyKey struct{} // context value is bool
|
|
||||||
)
|
|
@ -18,7 +18,6 @@ package sql
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"database/sql/internal"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -115,12 +114,10 @@ func Named(name string, value interface{}) NamedArg {
|
|||||||
return NamedArg{Name: name, Value: value}
|
return NamedArg{Name: name, Value: value}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsolationLevel is the transaction isolation level stored in Context.
|
// IsolationLevel is the transaction isolation level used in TxOptions.
|
||||||
// The IsolationLevel is set with IsolationContext and the context
|
|
||||||
// should be passed to BeginContext.
|
|
||||||
type IsolationLevel int
|
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.
|
// 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.
|
// See https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels.
|
||||||
@ -135,18 +132,12 @@ const (
|
|||||||
LevelLinearizable
|
LevelLinearizable
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsolationContext returns a new Context that carries the provided isolation level.
|
// TxOptions holds the transaction options to be used in DB.BeginTx.
|
||||||
// The context must contain the isolation level before beginning the transaction
|
type TxOptions struct {
|
||||||
// with BeginContext.
|
// Isolation is the transaction isolation level.
|
||||||
func IsolationContext(ctx context.Context, level IsolationLevel) context.Context {
|
// If zero, the driver or database's default level is used.
|
||||||
return context.WithValue(ctx, internal.IsolationLevelKey{}, driver.IsolationLevel(level))
|
Isolation IsolationLevel
|
||||||
}
|
ReadOnly bool
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawBytes is a byte slice that holds a reference to memory owned by
|
// 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...)
|
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.
|
// The provided context is used until the transaction is committed or rolled back.
|
||||||
// If the context is canceled, the sql package will roll 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
|
// 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
|
// The provided TxOptions is optional and may be nil if defaults should be used.
|
||||||
// before calling this. If a non-default isolation level is used
|
// If a non-default isolation level is used that the driver doesn't support,
|
||||||
// that the driver doesn't support an error will be returned. Different drivers
|
// an error will be returned.
|
||||||
// may have slightly different meanings for the same isolation level.
|
func (db *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error) {
|
||||||
func (db *DB) BeginContext(ctx context.Context) (*Tx, error) {
|
|
||||||
var tx *Tx
|
var tx *Tx
|
||||||
var err error
|
var err error
|
||||||
for i := 0; i < maxBadConnRetries; i++ {
|
for i := 0; i < maxBadConnRetries; i++ {
|
||||||
tx, err = db.begin(ctx, cachedOrNewConn)
|
tx, err = db.begin(ctx, opts, cachedOrNewConn)
|
||||||
if err != driver.ErrBadConn {
|
if err != driver.ErrBadConn {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == driver.ErrBadConn {
|
if err == driver.ErrBadConn {
|
||||||
return db.begin(ctx, alwaysNewConn)
|
return db.begin(ctx, opts, alwaysNewConn)
|
||||||
}
|
}
|
||||||
return tx, err
|
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
|
// Begin starts a transaction. The default isolation level is dependent on
|
||||||
// the driver.
|
// the driver.
|
||||||
func (db *DB) Begin() (*Tx, error) {
|
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)
|
dc, err := db.conn(ctx, strategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var txi driver.Tx
|
var txi driver.Tx
|
||||||
withLock(dc, func() {
|
withLock(dc, func() {
|
||||||
txi, err = ctxDriverBegin(ctx, dc.ci)
|
txi, err = ctxDriverBegin(ctx, opts, dc.ci)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
db.putConn(dc, err)
|
db.putConn(dc, err)
|
||||||
|
@ -375,7 +375,7 @@ func TestTxContextWait(t *testing.T) {
|
|||||||
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*15)
|
ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*15)
|
||||||
|
|
||||||
tx, err := db.BeginContext(ctx)
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user