diff options
author | Daniel Theophanes <kardianos@gmail.com> | 2016-12-13 07:55:12 -0800 |
---|---|---|
committer | Brad Fitzpatrick <bradfitz@golang.org> | 2016-12-14 18:13:13 +0000 |
commit | d0501f1da9c54a8053940feeb86e1644ffaae522 (patch) | |
tree | 35a699faceba0cf306bdf1a413d24828ac2e21da | |
parent | fe07091f9eb9a1dcedc6eab1762de5383f520a00 (diff) | |
download | go-d0501f1da9c54a8053940feeb86e1644ffaae522.tar.gz go-d0501f1da9c54a8053940feeb86e1644ffaae522.zip |
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>
-rw-r--r-- | api/go1.8.txt | 16 | ||||
-rw-r--r-- | src/database/sql/ctxutil.go | 31 | ||||
-rw-r--r-- | src/database/sql/driver/driver.go | 41 | ||||
-rw-r--r-- | src/database/sql/internal/types.go | 11 | ||||
-rw-r--r-- | src/database/sql/sql.go | 48 | ||||
-rw-r--r-- | src/database/sql/sql_test.go | 2 |
6 files changed, 65 insertions, 84 deletions
diff --git a/api/go1.8.txt b/api/go1.8.txt index 2356d72e2b..9493f2c33a 100644 --- a/api/go1.8.txt +++ b/api/go1.8.txt @@ -73,10 +73,8 @@ pkg database/sql, const LevelSnapshot = 5 pkg database/sql, const LevelSnapshot IsolationLevel pkg database/sql, const LevelWriteCommitted = 3 pkg database/sql, const LevelWriteCommitted IsolationLevel -pkg database/sql/driver, func IsolationFromContext(context.Context) (IsolationLevel, bool) -pkg database/sql/driver, func ReadOnlyFromContext(context.Context) bool -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 ConnBeginTx interface { BeginTx } +pkg database/sql/driver, type ConnBeginTx interface, BeginTx(context.Context, TxOptions) (Tx, error) 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 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 StmtQueryContext interface { QueryContext } 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 ReadOnlyContext(context.Context) context.Context pkg database/sql, method (*ColumnType) DatabaseTypeName() string pkg database/sql, method (*ColumnType) DecimalSize() (int64, int64, bool) pkg database/sql, method (*ColumnType) Length() (int64, bool) pkg database/sql, method (*ColumnType) Name() string pkg database/sql, method (*ColumnType) Nullable() (bool, bool) 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) PingContext(context.Context) 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, Name string 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 (StringTable) String(uint32) (string, error) pkg debug/pe, type File struct, COFFSymbols []COFFSymbol diff --git a/src/database/sql/ctxutil.go b/src/database/sql/ctxutil.go index 7c05ce2448..1071446227 100644 --- a/src/database/sql/ctxutil.go +++ b/src/database/sql/ctxutil.go @@ -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") -func ctxDriverBegin(ctx context.Context, ci driver.Conn) (driver.Tx, error) { - if ciCtx, is := ci.(driver.ConnBeginContext); is { - return ciCtx.BeginContext(ctx) +func ctxDriverBegin(ctx context.Context, opts *TxOptions, ci driver.Conn) (driver.Tx, error) { + if ciCtx, is := ci.(driver.ConnBeginTx); is { + 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() { return ci.Begin() } - // Check the transaction level in ctx. If set and non-default - // then return an error here as the BeginContext driver value is not supported. - if level, ok := driver.IsolationFromContext(ctx); ok && level != driver.IsolationLevel(LevelDefault) { - return nil, errors.New("sql: driver does not support non-default isolation level") - } + if opts != nil { + // Check the transaction level. If the transaction level is non-default + // then return an error here as the BeginTx driver value is not supported. + 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 - // requested return an error as the BeginContext driver value is not supported. - if ro := driver.ReadOnlyFromContext(ctx); ro { - return nil, errors.New("sql: driver does not support read-only transactions") + // If a read-only transaction is requested return an error as the + // BeginTx driver value is not supported. + if opts.ReadOnly { + return nil, errors.New("sql: driver does not support read-only transactions") + } } txi, err := ci.Begin() diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go index 2e47cd9ee7..d66196fd48 100644 --- a/src/database/sql/driver/driver.go +++ b/src/database/sql/driver/driver.go @@ -10,7 +10,6 @@ package driver import ( "context" - "database/sql/internal" "errors" "reflect" ) @@ -157,7 +156,7 @@ type Conn interface { // 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) } @@ -169,41 +168,35 @@ type ConnPrepareContext interface { 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 // with any values defined on it. type IsolationLevel int -// IsolationFromContext extracts the isolation level from a Context. -func IsolationFromContext(ctx context.Context) (level IsolationLevel, ok bool) { - level, ok = ctx.Value(internal.IsolationLevelKey{}).(IsolationLevel) - return level, ok -} - -// ReadOnlyFromContext extracts the read-only property from a Context. -// When readonly is true the transaction must be set to read-only -// or return an error. -func ReadOnlyFromContext(ctx context.Context) (readonly bool) { - readonly, _ = ctx.Value(internal.ReadOnlyKey{}).(bool) - return readonly +// TxOptions holds the transaction options. +// +// This type should be considered identical to sql.TxOptions. +type TxOptions struct { + Isolation IsolationLevel + ReadOnly bool } -// ConnBeginContext enhances the Conn interface with context. -type ConnBeginContext interface { - // BeginContext starts and returns a new transaction. +// ConnBeginTx enhances the Conn interface with context and TxOptions. +type ConnBeginTx interface { + // BeginTx starts and returns a new transaction. // If the context is canceled by the user the sql package will // call Tx.Rollback before discarding and closing the connection. // - // This must call IsolationFromContext to determine if there is a set - // isolation level. If the driver does not support setting the isolation - // level and one is set or if there is a set isolation level - // but the set level is not supported, an error must be returned. + // This must check opts.Isolation to determine if there is a set + // isolation level. If the driver does not support a non-default + // level and one is set or if there is a non-default isolation level + // 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 // 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. diff --git a/src/database/sql/internal/types.go b/src/database/sql/internal/types.go deleted file mode 100644 index 1895144cb2..0000000000 --- a/src/database/sql/internal/types.go +++ /dev/null @@ -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 -) diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index 9f056c51e2..960245065e 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -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) diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index 1ec6217858..422d2198ba 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -375,7 +375,7 @@ func TestTxContextWait(t *testing.T) { ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*15) - tx, err := db.BeginContext(ctx) + tx, err := db.BeginTx(ctx, nil) if err != nil { t.Fatal(err) } |