jmoiron / sqlx

general purpose extensions to golang's database/sql
http://jmoiron.github.io/sqlx/
MIT License
16.3k stars 1.09k forks source link

Call to sqlx.In on driver.Value with nil value panics #953

Open moxar opened 1 month ago

moxar commented 1 month ago

Description

When calling sqlx.In on a pointer to var that implements driver.Valuer interface, the function panics for nil pointer dereference.

Current behaviour

type Valuer struct {
    Val string
}

func (v Valuer) Value() (driver.Value, error) {
    return v.Val, nil
}

query := `SELECT * FROM foo WHERE x = ? or y IN (?)`
        _, _, err := In(query,
            (*Valuer)(nil), // a non-inited pointer to valuer
            []interface{}{
                "a",                 // a usual value
                nil,                 // a nil value
                Valuer{Val: "foo"},  // a Valuer
                &Valuer{Val: "foo"}, // a pointer to valuer
                (*Valuer)(nil),      // a non-inited pointer to valuer
            },
        )
--- FAIL: TestIn (0.00s)
    --- FAIL: TestIn/with_nil_driver.Valuer (0.00s)
panic: value method github.com/jmoiron/sqlx.Valuer.Value called using nil *Valuer pointer [recovered]
        panic: value method github.com/jmoiron/sqlx.Valuer.Value called using nil *Valuer pointer

goroutine 13 [running]:
testing.tRunner.func1.2({0x8452a0, 0xc0001d5320})
        /usr/local/go/src/testing/testing.go:1632 +0x230
testing.tRunner.func1()
        /usr/local/go/src/testing/testing.go:1635 +0x35e
panic({0x8452a0?, 0xc0001d5320?})
        /usr/local/go/src/runtime/panic.go:785 +0x132
github.com/jmoiron/sqlx.(*Valuer).Value(0xb48390?)
        <autogenerated>:1 +0x45
github.com/jmoiron/sqlx.In({0x8af3e6, 0x29}, {0xc0000d9f30, 0x2, 0x2})
        /home/athomas/Work/sqlx/bind.go:166 +0x157
github.com/jmoiron/sqlx.TestIn.func1(0xc0000aad00)
        /home/athomas/Work/sqlx/sqlx_test.go:1552 +0x154
testing.tRunner(0xc0000aad00, 0x8d1b60)
        /usr/local/go/src/testing/testing.go:1690 +0xf4
created by testing.(*T).Run in goroutine 12
        /usr/local/go/src/testing/testing.go:1743 +0x390
FAIL    github.com/jmoiron/sqlx 0.006s

expected behaviour

The call should not panic. In stdlib, the resolution of a driver.Valuer returns nil in the special case of a nil value, as decribed in this snippet. I would expect sqlx to behave like stdlib on this matter.

// callValuerValue returns vr.Value(), with one exception:
// If vr.Value is an auto-generated method on a pointer type and the
// pointer is nil, it would panic at runtime in the panicwrap
// method. Treat it like nil instead.
// Issue 8415.
//
// This is so people can implement driver.Value on value types and
// still use nil pointers to those types to mean nil/NULL, just like
// string/*string.
//
// This function is mirrored in the database/sql package.
func callValuerValue(vr Valuer) (v Value, err error) {
    if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Pointer &&
        rv.IsNil() &&
        rv.Type().Elem().Implements(valuerReflectType) {
        return nil, nil
    }
    return vr.Value()
}
--- PASS: TestIn (0.00s)
    --- PASS: TestIn/with_nil_driver.Valuer (0.00s)