<p dir="auto">by <strong>john.corbett</strong>:</p>
<pre class="notranslate">What steps will reproduce the problem?

Try to look up go, or any other stop word in a search engine.
Notice the results are garbage.

Please provide any additional information below.

Please do not name your thing after a common term or a term already
strongly associated with something else. Even if you succeed in displacing
a established meaning, you don't win--you just offend people.

Other examples of dumb names, in case you need to be hit over the head with
it: C, C++, X, widget, R, S, NeXT, and processing. I'm sure that now that
you have the idea you can generate more bad names yourself, but please don't.

Thanks,
  John</pre>
<p dir="auto">go version go1.4.2 windows/amd64</p>
<p dir="auto">This issue was originally discovered while using github.com/alexbrainman/odbc, but it does not seem to be a problem specific to that driver. It is minimally reproducible with the below test program.</p>
<p dir="auto">The program runs a query inside a transaction five times. The commit (or rollback) of the second attempt returns <code class="notranslate">driver.ErrBadConn</code>.</p>
<p dir="auto">I expect the bad connection to get removed from the pool and subsequent transactions to occur on a fresh connection.</p>
<p dir="auto">Instead database/sql keeps the bad connection in the pool and subsequent transactions reuse the bad connection and also fail.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="package main

import (
    &quot;database/sql&quot;
    &quot;database/sql/driver&quot;
    &quot;log&quot;
    &quot;reflect&quot;
    &quot;runtime&quot;
)

var opened, closed int

type MockDriver struct{}

func (d *MockDriver) Open(name string) (driver.Conn, error) {
    opened++
    return &amp;MockConn{}, NextError()
}

type MockConn struct{ err error }

// NOTE: that this always succeeds, even if c.err != nil this simulates a
// driver that doesn't need to communicate the beginning of a transaction and
// therefore may not be able to discover a bad connection at this point.
func (c *MockConn) Begin() (driver.Tx, error) { return &amp;MockTx{c}, nil }

func (c *MockConn) Close() error                              { closed++; return c.Err() }
func (c *MockConn) Prepare(query string) (driver.Stmt, error) { return &amp;MockStmt{c}, c.Err() }
func (c *MockConn) Err() error {
    if c.err == nil {
        c.err = NextError()
    }
    return c.err
}

type MockTx struct{ c *MockConn }

func (tx *MockTx) Commit() error   { return tx.c.Err() }
func (tx *MockTx) Rollback() error { return tx.c.Err() }

type MockStmt struct{ c *MockConn }

func (s *MockStmt) Close() error  { return s.c.Err() }
func (s *MockStmt) NumInput() int { return -1 }
func (s *MockStmt) Exec(args []driver.Value) (driver.Result, error) {
    return driver.ResultNoRows, s.c.Err()
}
func (s *MockStmt) Query(args []driver.Value) (driver.Rows, error) { return &amp;MockRows{s}, s.c.Err() }

type MockRows struct{ s *MockStmt }

func (r *MockRows) Columns() []string              { return []string{} }
func (r *MockRows) Close() error                   { return r.s.c.Err() }
func (r *MockRows) Next(dest []driver.Value) error { return r.s.c.Err() }

var nextError error

func NextError() (err error) {
    err, nextError = nextError, nil
    return
}

func main() {
    drv := &amp;MockDriver{}
    sql.Register(&quot;mock&quot;, drv)
    db, err := sql.Open(&quot;mock&quot;, &quot;&quot;)
    checkErr(0, err, nil)
    for i := 0; i &lt; 5; i++ {
        // this always succeeds because that's the way some drivers may work
        tx, err := db.Begin()
        checkErr(i, err, nil)
        if err != nil {
            continue
        }
        rows, err := tx.Query(&quot;query&quot;)
        checkErr(i, err, nil)
        if err != nil {
            err = tx.Rollback()
            checkErr(i, err, nil)
            continue
        }
        rows.Close()
        var want error
        if i == 1 {
            nextError, want = driver.ErrBadConn, driver.ErrBadConn
        }
        // fails when i == 1, but the connection is not removed from the pool
        err = tx.Commit()
        checkErr(i, err, want)
        log.Printf(&quot;i(%d), opened: %d, closed: %d&quot;, i, opened, closed)
    }
    log.Printf(&quot;final, opened: %d, closed: %d&quot;, opened, closed)
}

func checkErr(i int, got, want error) {
    if !reflect.DeepEqual(got, want) {
        _, _, line, _ := runtime.Caller(1)
        log.Printf(&quot;i(%d), line %d: got %v, want %v&quot;, i, line, got, want)
    }
}"><pre class="notranslate"><span class="pl-k">package</span> main

<span class="pl-k">import</span> (
    <span class="pl-s">"database/sql"</span>
    <span class="pl-s">"database/sql/driver"</span>
    <span class="pl-s">"log"</span>
    <span class="pl-s">"reflect"</span>
    <span class="pl-s">"runtime"</span>
)

<span class="pl-k">var</span> <span class="pl-s1">opened</span>, <span class="pl-s1">closed</span> <span class="pl-smi">int</span>

<span class="pl-k">type</span> <span class="pl-smi">MockDriver</span> <span class="pl-k">struct</span>{}

<span class="pl-k">func</span> (<span class="pl-s1">d</span> <span class="pl-c1">*</span><span class="pl-smi">MockDriver</span>) <span class="pl-en">Open</span>(<span class="pl-s1">name</span> <span class="pl-smi">string</span>) (driver.<span class="pl-smi">Conn</span>, <span class="pl-smi">error</span>) {
    <span class="pl-s1">opened</span><span class="pl-c1">++</span>
    <span class="pl-k">return</span> <span class="pl-c1">&amp;</span><span class="pl-smi">MockConn</span>{}, <span class="pl-en">NextError</span>()
}

<span class="pl-k">type</span> <span class="pl-smi">MockConn</span> <span class="pl-k">struct</span>{ <span class="pl-c1">err</span> <span class="pl-smi">error</span> }

<span class="pl-c">// NOTE: that this always succeeds, even if c.err != nil this simulates a</span>
<span class="pl-c">// driver that doesn't need to communicate the beginning of a transaction and</span>
<span class="pl-c">// therefore may not be able to discover a bad connection at this point.</span>
<span class="pl-k">func</span> (<span class="pl-s1">c</span> <span class="pl-c1">*</span><span class="pl-smi">MockConn</span>) <span class="pl-en">Begin</span>() (driver.<span class="pl-smi">Tx</span>, <span class="pl-smi">error</span>) { <span class="pl-k">return</span> <span class="pl-c1">&amp;</span><span class="pl-smi">MockTx</span>{<span class="pl-s1">c</span>}, <span class="pl-c1">nil</span> }

<span class="pl-k">func</span> (<span class="pl-s1">c</span> <span class="pl-c1">*</span><span class="pl-smi">MockConn</span>) <span class="pl-en">Close</span>() <span class="pl-smi">error</span>                              { <span class="pl-s1">closed</span><span class="pl-c1">++</span>; <span class="pl-k">return</span> <span class="pl-s1">c</span>.<span class="pl-en">Err</span>() }
<span class="pl-k">func</span> (<span class="pl-s1">c</span> <span class="pl-c1">*</span><span class="pl-smi">MockConn</span>) <span class="pl-en">Prepare</span>(<span class="pl-s1">query</span> <span class="pl-smi">string</span>) (driver.<span class="pl-smi">Stmt</span>, <span class="pl-smi">error</span>) { <span class="pl-k">return</span> <span class="pl-c1">&amp;</span><span class="pl-smi">MockStmt</span>{<span class="pl-s1">c</span>}, <span class="pl-s1">c</span>.<span class="pl-en">Err</span>() }
<span class="pl-k">func</span> (<span class="pl-s1">c</span> <span class="pl-c1">*</span><span class="pl-smi">MockConn</span>) <span class="pl-en">Err</span>() <span class="pl-smi">error</span> {
    <span class="pl-k">if</span> <span class="pl-s1">c</span>.<span class="pl-c1">err</span> <span class="pl-c1">==</span> <span class="pl-c1">nil</span> {
        <span class="pl-s1">c</span>.<span class="pl-c1">err</span> <span class="pl-c1">=</span> <span class="pl-en">NextError</span>()
    }
    <span class="pl-k">return</span> <span class="pl-s1">c</span>.<span class="pl-c1">err</span>
}

<span class="pl-k">type</span> <span class="pl-smi">MockTx</span> <span class="pl-k">struct</span>{ <span class="pl-c1">c</span> <span class="pl-c1">*</span><span class="pl-smi">MockConn</span> }

<span class="pl-k">func</span> (<span class="pl-s1">tx</span> <span class="pl-c1">*</span><span class="pl-smi">MockTx</span>) <span class="pl-en">Commit</span>() <span class="pl-smi">error</span>   { <span class="pl-k">return</span> <span class="pl-s1">tx</span>.<span class="pl-c1">c</span>.<span class="pl-en">Err</span>() }
<span class="pl-k">func</span> (<span class="pl-s1">tx</span> <span class="pl-c1">*</span><span class="pl-smi">MockTx</span>) <span class="pl-en">Rollback</span>() <span class="pl-smi">error</span> { <span class="pl-k">return</span> <span class="pl-s1">tx</span>.<span class="pl-c1">c</span>.<span class="pl-en">Err</span>() }

<span class="pl-k">type</span> <span class="pl-smi">MockStmt</span> <span class="pl-k">struct</span>{ <span class="pl-c1">c</span> <span class="pl-c1">*</span><span class="pl-smi">MockConn</span> }

<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">MockStmt</span>) <span class="pl-en">Close</span>() <span class="pl-smi">error</span>  { <span class="pl-k">return</span> <span class="pl-s1">s</span>.<span class="pl-c1">c</span>.<span class="pl-en">Err</span>() }
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">MockStmt</span>) <span class="pl-en">NumInput</span>() <span class="pl-smi">int</span> { <span class="pl-k">return</span> <span class="pl-c1">-</span><span class="pl-c1">1</span> }
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">MockStmt</span>) <span class="pl-en">Exec</span>(<span class="pl-s1">args</span> []driver.<span class="pl-smi">Value</span>) (driver.<span class="pl-smi">Result</span>, <span class="pl-smi">error</span>) {
    <span class="pl-k">return</span> <span class="pl-s1">driver</span>.<span class="pl-c1">ResultNoRows</span>, <span class="pl-s1">s</span>.<span class="pl-c1">c</span>.<span class="pl-en">Err</span>()
}
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">MockStmt</span>) <span class="pl-en">Query</span>(<span class="pl-s1">args</span> []driver.<span class="pl-smi">Value</span>) (driver.<span class="pl-smi">Rows</span>, <span class="pl-smi">error</span>) { <span class="pl-k">return</span> <span class="pl-c1">&amp;</span><span class="pl-smi">MockRows</span>{<span class="pl-s1">s</span>}, <span class="pl-s1">s</span>.<span class="pl-c1">c</span>.<span class="pl-en">Err</span>() }

<span class="pl-k">type</span> <span class="pl-smi">MockRows</span> <span class="pl-k">struct</span>{ <span class="pl-c1">s</span> <span class="pl-c1">*</span><span class="pl-smi">MockStmt</span> }

<span class="pl-k">func</span> (<span class="pl-s1">r</span> <span class="pl-c1">*</span><span class="pl-smi">MockRows</span>) <span class="pl-en">Columns</span>() []<span class="pl-smi">string</span>              { <span class="pl-k">return</span> []<span class="pl-smi">string</span>{} }
<span class="pl-k">func</span> (<span class="pl-s1">r</span> <span class="pl-c1">*</span><span class="pl-smi">MockRows</span>) <span class="pl-en">Close</span>() <span class="pl-smi">error</span>                   { <span class="pl-k">return</span> <span class="pl-s1">r</span>.<span class="pl-c1">s</span>.<span class="pl-c1">c</span>.<span class="pl-en">Err</span>() }
<span class="pl-k">func</span> (<span class="pl-s1">r</span> <span class="pl-c1">*</span><span class="pl-smi">MockRows</span>) <span class="pl-en">Next</span>(<span class="pl-s1">dest</span> []driver.<span class="pl-smi">Value</span>) <span class="pl-smi">error</span> { <span class="pl-k">return</span> <span class="pl-s1">r</span>.<span class="pl-c1">s</span>.<span class="pl-c1">c</span>.<span class="pl-en">Err</span>() }

<span class="pl-k">var</span> <span class="pl-s1">nextError</span> <span class="pl-smi">error</span>

<span class="pl-k">func</span> <span class="pl-en">NextError</span>() (<span class="pl-s1">err</span> <span class="pl-smi">error</span>) {
    <span class="pl-s1">err</span>, <span class="pl-s1">nextError</span> <span class="pl-c1">=</span> <span class="pl-s1">nextError</span>, <span class="pl-c1">nil</span>
    <span class="pl-k">return</span>
}

<span class="pl-k">func</span> <span class="pl-en">main</span>() {
    <span class="pl-s1">drv</span> <span class="pl-c1">:=</span> <span class="pl-c1">&amp;</span><span class="pl-smi">MockDriver</span>{}
    <span class="pl-s1">sql</span>.<span class="pl-en">Register</span>(<span class="pl-s">"mock"</span>, <span class="pl-s1">drv</span>)
    <span class="pl-s1">db</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">sql</span>.<span class="pl-en">Open</span>(<span class="pl-s">"mock"</span>, <span class="pl-s">""</span>)
    <span class="pl-en">checkErr</span>(<span class="pl-c1">0</span>, <span class="pl-s1">err</span>, <span class="pl-c1">nil</span>)
    <span class="pl-k">for</span> <span class="pl-s1">i</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">i</span> <span class="pl-c1">&lt;</span> <span class="pl-c1">5</span>; <span class="pl-s1">i</span><span class="pl-c1">++</span> {
        <span class="pl-c">// this always succeeds because that's the way some drivers may work</span>
        <span class="pl-s1">tx</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">db</span>.<span class="pl-en">Begin</span>()
        <span class="pl-en">checkErr</span>(<span class="pl-s1">i</span>, <span class="pl-s1">err</span>, <span class="pl-c1">nil</span>)
        <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
            <span class="pl-k">continue</span>
        }
        <span class="pl-s1">rows</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">tx</span>.<span class="pl-en">Query</span>(<span class="pl-s">"query"</span>)
        <span class="pl-en">checkErr</span>(<span class="pl-s1">i</span>, <span class="pl-s1">err</span>, <span class="pl-c1">nil</span>)
        <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
            <span class="pl-s1">err</span> <span class="pl-c1">=</span> <span class="pl-s1">tx</span>.<span class="pl-en">Rollback</span>()
            <span class="pl-en">checkErr</span>(<span class="pl-s1">i</span>, <span class="pl-s1">err</span>, <span class="pl-c1">nil</span>)
            <span class="pl-k">continue</span>
        }
        <span class="pl-s1">rows</span>.<span class="pl-en">Close</span>()
        <span class="pl-k">var</span> <span class="pl-s1">want</span> <span class="pl-smi">error</span>
        <span class="pl-k">if</span> <span class="pl-s1">i</span> <span class="pl-c1">==</span> <span class="pl-c1">1</span> {
            <span class="pl-s1">nextError</span>, <span class="pl-s1">want</span> <span class="pl-c1">=</span> <span class="pl-s1">driver</span>.<span class="pl-c1">ErrBadConn</span>, <span class="pl-s1">driver</span>.<span class="pl-c1">ErrBadConn</span>
        }
        <span class="pl-c">// fails when i == 1, but the connection is not removed from the pool</span>
        <span class="pl-s1">err</span> <span class="pl-c1">=</span> <span class="pl-s1">tx</span>.<span class="pl-en">Commit</span>()
        <span class="pl-en">checkErr</span>(<span class="pl-s1">i</span>, <span class="pl-s1">err</span>, <span class="pl-s1">want</span>)
        <span class="pl-s1">log</span>.<span class="pl-en">Printf</span>(<span class="pl-s">"i(%d), opened: %d, closed: %d"</span>, <span class="pl-s1">i</span>, <span class="pl-s1">opened</span>, <span class="pl-s1">closed</span>)
    }
    <span class="pl-s1">log</span>.<span class="pl-en">Printf</span>(<span class="pl-s">"final, opened: %d, closed: %d"</span>, <span class="pl-s1">opened</span>, <span class="pl-s1">closed</span>)
}

<span class="pl-k">func</span> <span class="pl-en">checkErr</span>(<span class="pl-s1">i</span> <span class="pl-smi">int</span>, <span class="pl-s1">got</span>, <span class="pl-s1">want</span> <span class="pl-smi">error</span>) {
    <span class="pl-k">if</span> <span class="pl-c1">!</span><span class="pl-s1">reflect</span>.<span class="pl-en">DeepEqual</span>(<span class="pl-s1">got</span>, <span class="pl-s1">want</span>) {
        <span class="pl-s1">_</span>, <span class="pl-s1">_</span>, <span class="pl-s1">line</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">runtime</span>.<span class="pl-en">Caller</span>(<span class="pl-c1">1</span>)
        <span class="pl-s1">log</span>.<span class="pl-en">Printf</span>(<span class="pl-s">"i(%d), line %d: got %v, want %v"</span>, <span class="pl-s1">i</span>, <span class="pl-s1">line</span>, <span class="pl-s1">got</span>, <span class="pl-s1">want</span>)
    }
}</pre></div>