Do not release queries in Get/Select. Fix #25
Releasing query objects in Get/Select could easily lead to double-releases, which can cause dangerous and tricky data races. Remove the query field of Iterx and its usages (all release-related). This is a breaking API change, because it removes the exported method ReleaseQuery. Update documenting examples to demonstrate the deferred query release pattern clients can use to manage query release.
This commit is contained in:
committed by
Michał Matczuk
parent
255429a93c
commit
cbe2659e0d
@@ -146,6 +146,7 @@ func TestExample(t *testing.T) {
|
|||||||
q := gocqlx.Query(session.Query(stmt), names).BindMap(qb.M{
|
q := gocqlx.Query(session.Query(stmt), names).BindMap(qb.M{
|
||||||
"first_name": "Patricia",
|
"first_name": "Patricia",
|
||||||
})
|
})
|
||||||
|
defer q.Release()
|
||||||
|
|
||||||
var p Person
|
var p Person
|
||||||
if err := gocqlx.Get(&p, q.Query); err != nil {
|
if err := gocqlx.Get(&p, q.Query); err != nil {
|
||||||
@@ -165,6 +166,7 @@ func TestExample(t *testing.T) {
|
|||||||
q := gocqlx.Query(session.Query(stmt), names).BindMap(qb.M{
|
q := gocqlx.Query(session.Query(stmt), names).BindMap(qb.M{
|
||||||
"first_name": []string{"Patricia", "Igy", "Ian"},
|
"first_name": []string{"Patricia", "Igy", "Ian"},
|
||||||
})
|
})
|
||||||
|
defer q.Release()
|
||||||
|
|
||||||
var people []Person
|
var people []Person
|
||||||
if err := gocqlx.Select(&people, q.Query); err != nil {
|
if err := gocqlx.Select(&people, q.Query); err != nil {
|
||||||
@@ -190,6 +192,7 @@ func TestExample(t *testing.T) {
|
|||||||
ToCql()
|
ToCql()
|
||||||
|
|
||||||
q := gocqlx.Query(session.Query(stmt), names).BindStruct(p)
|
q := gocqlx.Query(session.Query(stmt), names).BindStruct(p)
|
||||||
|
defer q.Release()
|
||||||
|
|
||||||
var people []Person
|
var people []Person
|
||||||
if err := gocqlx.Select(&people, q.Query); err != nil {
|
if err := gocqlx.Select(&people, q.Query); err != nil {
|
||||||
|
|||||||
26
iterx.go
26
iterx.go
@@ -26,7 +26,6 @@ func Select(dest interface{}, q *gocql.Query) error {
|
|||||||
// Iterx is a wrapper around gocql.Iter which adds struct scanning capabilities.
|
// Iterx is a wrapper around gocql.Iter which adds struct scanning capabilities.
|
||||||
type Iterx struct {
|
type Iterx struct {
|
||||||
*gocql.Iter
|
*gocql.Iter
|
||||||
query *gocql.Query
|
|
||||||
err error
|
err error
|
||||||
|
|
||||||
unsafe bool
|
unsafe bool
|
||||||
@@ -41,7 +40,6 @@ type Iterx struct {
|
|||||||
func Iter(q *gocql.Query) *Iterx {
|
func Iter(q *gocql.Query) *Iterx {
|
||||||
return &Iterx{
|
return &Iterx{
|
||||||
Iter: q.Iter(),
|
Iter: q.Iter(),
|
||||||
query: q,
|
|
||||||
Mapper: DefaultMapper,
|
Mapper: DefaultMapper,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,13 +57,8 @@ func (iter *Iterx) Unsafe() *Iterx {
|
|||||||
// destination is some other type, then the row must only have one column which
|
// destination is some other type, then the row must only have one column which
|
||||||
// can scan into that type. If no rows were selected, ErrNotFound is returned.
|
// can scan into that type. If no rows were selected, ErrNotFound is returned.
|
||||||
func (iter *Iterx) Get(dest interface{}) error {
|
func (iter *Iterx) Get(dest interface{}) error {
|
||||||
if iter.query == nil {
|
|
||||||
return errors.New("using released query")
|
|
||||||
}
|
|
||||||
|
|
||||||
iter.scanAny(dest, false)
|
iter.scanAny(dest, false)
|
||||||
iter.Close()
|
iter.Close()
|
||||||
iter.ReleaseQuery()
|
|
||||||
|
|
||||||
return iter.checkErrAndNotFound()
|
return iter.checkErrAndNotFound()
|
||||||
}
|
}
|
||||||
@@ -111,13 +104,8 @@ func (iter *Iterx) scanAny(dest interface{}, structOnly bool) bool {
|
|||||||
// StructScan will be used on each row. If the destination is some other type,
|
// StructScan will be used on each row. If the destination is some other type,
|
||||||
// then each row must only have one column which can scan into that type.
|
// then each row must only have one column which can scan into that type.
|
||||||
func (iter *Iterx) Select(dest interface{}) error {
|
func (iter *Iterx) Select(dest interface{}) error {
|
||||||
if iter.query == nil {
|
|
||||||
return errors.New("using released query")
|
|
||||||
}
|
|
||||||
|
|
||||||
iter.scanAll(dest, false)
|
iter.scanAll(dest, false)
|
||||||
iter.Close()
|
iter.Close()
|
||||||
iter.ReleaseQuery()
|
|
||||||
|
|
||||||
return iter.err
|
return iter.err
|
||||||
}
|
}
|
||||||
@@ -209,11 +197,6 @@ func (iter *Iterx) scanAll(dest interface{}, structOnly bool) bool {
|
|||||||
// safe to run StructScan on the same Iterx instance with different struct
|
// safe to run StructScan on the same Iterx instance with different struct
|
||||||
// types.
|
// types.
|
||||||
func (iter *Iterx) StructScan(dest interface{}) bool {
|
func (iter *Iterx) StructScan(dest interface{}) bool {
|
||||||
if iter.query == nil {
|
|
||||||
iter.err = errors.New("using released query")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
v := reflect.ValueOf(dest)
|
v := reflect.ValueOf(dest)
|
||||||
if v.Kind() != reflect.Ptr {
|
if v.Kind() != reflect.Ptr {
|
||||||
iter.err = errors.New("must pass a pointer, not a value, to StructScan destination")
|
iter.err = errors.New("must pass a pointer, not a value, to StructScan destination")
|
||||||
@@ -268,15 +251,6 @@ func (iter *Iterx) Close() error {
|
|||||||
return iter.err
|
return iter.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReleaseQuery releases underling query back into a pool of queries. Note that
|
|
||||||
// the iterator needs to be closed first.
|
|
||||||
func (iter *Iterx) ReleaseQuery() {
|
|
||||||
if iter.query != nil {
|
|
||||||
iter.query.Release()
|
|
||||||
iter.query = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkErrAndNotFound handle error and NotFound in one method.
|
// checkErrAndNotFound handle error and NotFound in one method.
|
||||||
func (iter *Iterx) checkErrAndNotFound() error {
|
func (iter *Iterx) checkErrAndNotFound() error {
|
||||||
if iter.err != nil {
|
if iter.err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user