db icon indicating copy to clipboard operation
db copied to clipboard

Panic in valuer causes a hang with Iterator().All(&something) swallowing panic message

Open c2h5oh opened this issue 6 years ago • 1 comments


type broken string

func (b broken) Value() (driver.Value, error) {
    panic("this is a broken valuer")
    return nil,nil
}

val := broken("record")

query := conn.Select("*").From("mytable").Where(db.Cond{"column_name": val})

// this will panic
_, err := query.Iterator()

var ret []*sometype

 // this will hang, no panic message will be logged
err = query.Iterator().All(&ret)

c2h5oh avatar Jul 08 '19 12:07 c2h5oh

Hello @c2h5oh,

This looks a lot like: https://github.com/golang/go/issues/26332, in that issue we have a reference to https://golang.org/cl/170700, where they recommend this:

Most uses of the Valuer will be trivial, like returning a struct field. Optimize for that case. If sometime may panic the valuer should itself use recover and return an error.

However this only happens if driver.Valuer panics within a transaction, could you confirm if that was your case?

I added two tests to see how this affects upper, in the first case we're querying the database directly and we get a panic, and in the second case we're doing the same using a transaction and the program hangs-up: https://github.com/upper/db/pull/510/files

Here's another test using database/sql directly that hangs-up the program:

package main

import (
        "database/sql"
        "database/sql/driver"
        _ "github.com/mattn/go-sqlite3"
        "log"
)

type broken string

func (b broken) Value() (driver.Value, error) {
        panic("broken valuer")
        return nil, nil
}

func main() {
        db, err := sql.Open("sqlite3", "file:locked.sqlite?cache=shared")
        if err != nil {
                log.Fatal("sql.Open:", err)
        }

        value := broken("anything")

        tx, err := db.Begin()
        if err != nil {
                log.Fatal("db.Begin:", err)
        }
        defer tx.Rollback()

        log.Printf("here we go...")
        rows, err := tx.Query("SELECT $1", value)
        defer rows.Close()

        log.Printf("this line is never reached")

        if err != nil {
                log.Fatal("db.Query:", err)
        }

        rows.Next()
        var one string
        if err := rows.Scan(&one); err != nil {
                log.Fatal("rows.Scan:", err)
        }

        log.Printf("one: %v", one)
}

Could you confirm if that looks like the issue you're reporting?

xiam avatar Jul 13 '19 16:07 xiam