database/sql: record the context error in Rows if canceled

Previously it was intended that Rows.Scan would return
an error and Rows.Err would return nil. This was problematic
because drivers could not differentiate between a normal
Rows.Close or a context cancel close.

The alternative is to require drivers to return a Scan to return
an error if the driver is closed while there are still rows to be read.
This is currently not how several drivers currently work and may be
difficult to detect when there are additional rows.

At the same time guard the the Rows.lasterr and prevent a close
while a Rows operation is active.

For the drivers that do not have Context methods, do not check for
context cancelation after the operation, but before for any operation
that may modify the database state.

Fixes #18961

Change-Id: I49a25318ecd9f97a35d5b50540ecd850c01cfa5e
Reviewed-on: https://go-review.googlesource.com/36485
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Daniel Theophanes 2017-02-07 10:19:02 -08:00 committed by Russ Cox
parent 0c9325e13d
commit c026845bd2
3 changed files with 100 additions and 66 deletions

View file

@ -35,15 +35,12 @@ func ctxDriverExec(ctx context.Context, execer driver.Execer, query string, nvda
return nil, err
}
resi, err := execer.Exec(query, dargs)
if err == nil {
select {
default:
case <-ctx.Done():
return resi, ctx.Err()
}
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
return resi, err
return execer.Exec(query, dargs)
}
func ctxDriverQuery(ctx context.Context, queryer driver.Queryer, query string, nvdargs []driver.NamedValue) (driver.Rows, error) {
@ -56,16 +53,12 @@ func ctxDriverQuery(ctx context.Context, queryer driver.Queryer, query string, n
return nil, err
}
rowsi, err := queryer.Query(query, dargs)
if err == nil {
select {
default:
case <-ctx.Done():
rowsi.Close()
return nil, ctx.Err()
}
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
return rowsi, err
return queryer.Query(query, dargs)
}
func ctxDriverStmtExec(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Result, error) {
@ -77,15 +70,12 @@ func ctxDriverStmtExec(ctx context.Context, si driver.Stmt, nvdargs []driver.Nam
return nil, err
}
resi, err := si.Exec(dargs)
if err == nil {
select {
default:
case <-ctx.Done():
return resi, ctx.Err()
}
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
return resi, err
return si.Exec(dargs)
}
func ctxDriverStmtQuery(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Rows, error) {
@ -97,16 +87,12 @@ func ctxDriverStmtQuery(ctx context.Context, si driver.Stmt, nvdargs []driver.Na
return nil, err
}
rowsi, err := si.Query(dargs)
if err == nil {
select {
default:
case <-ctx.Done():
rowsi.Close()
return nil, ctx.Err()
}
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
return rowsi, err
return si.Query(dargs)
}
var errLevelNotSupported = errors.New("sql: selected isolation level is not supported")