src/database/sql/sql.go | 7 +++++++ src/database/sql/sql_test.go | 6 +++++- diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index a0b7ca8f0875a0e71355449b32ea4409300d78e5..74447d1945eb2dbe400830d2a7ecb73f6107e271 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -2729,8 +2729,15 @@ } return err } +// bypassRowsAwaitDone is only used for testing. +// If true, it will not close the Rows automatically from the context. +var bypassRowsAwaitDone = false + func (rs *Rows) initContextClose(ctx, txctx context.Context) { if ctx.Done() == nil && (txctx == nil || txctx.Done() == nil) { + return + } + if bypassRowsAwaitDone { return } ctx, rs.cancel = context.WithCancel(ctx) diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index a9e18004fb4a308f5d4ff421924fb09e6e92309b..7be5fc917a32315bc10300e9fb34a3a5f0954f15 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -2724,7 +2724,7 @@ t.Fatal(err) } } -// Issue 34755: Ensure that a Tx cannot commit after a rollback. +// Issue 34775: Ensure that a Tx cannot commit after a rollback. func TestTxCannotCommitAfterRollback(t *testing.T) { db := newTestDB(t, "tx_status") defer closeDB(t, db) @@ -2766,6 +2766,9 @@ // 1. Begin a transaction. // 2. (A) Start a query, (B) begin Tx rollback through a ctx cancel. // 3. Check if 2.A has committed in Tx (pass) or outside of Tx (fail). sendQuery := make(chan struct{}) + // The Tx status is returned through the row results, ensure + // that the rows results are not cancelled. + bypassRowsAwaitDone = true hookTxGrabConn = func() { cancel() <-sendQuery @@ -2776,6 +2779,7 @@ } defer func() { hookTxGrabConn = nil rollbackHook = nil + bypassRowsAwaitDone = false }() err = tx.QueryRow("SELECT|tx_status|tx_status|").Scan(&txStatus)