From cbe2659e0d60f3b70946b1b42f56e682a1d1b451 Mon Sep 17 00:00:00 2001 From: Josh Giles Date: Sat, 11 Nov 2017 09:27:15 -0500 Subject: [PATCH] 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. --- example_test.go | 3 +++ iterx.go | 28 +--------------------------- 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/example_test.go b/example_test.go index 6ebfb4d..50ca51e 100644 --- a/example_test.go +++ b/example_test.go @@ -146,6 +146,7 @@ func TestExample(t *testing.T) { q := gocqlx.Query(session.Query(stmt), names).BindMap(qb.M{ "first_name": "Patricia", }) + defer q.Release() var p Person 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{ "first_name": []string{"Patricia", "Igy", "Ian"}, }) + defer q.Release() var people []Person if err := gocqlx.Select(&people, q.Query); err != nil { @@ -190,6 +192,7 @@ func TestExample(t *testing.T) { ToCql() q := gocqlx.Query(session.Query(stmt), names).BindStruct(p) + defer q.Release() var people []Person if err := gocqlx.Select(&people, q.Query); err != nil { diff --git a/iterx.go b/iterx.go index 23ea557..6594296 100644 --- a/iterx.go +++ b/iterx.go @@ -26,8 +26,7 @@ func Select(dest interface{}, q *gocql.Query) error { // Iterx is a wrapper around gocql.Iter which adds struct scanning capabilities. type Iterx struct { *gocql.Iter - query *gocql.Query - err error + err error unsafe bool Mapper *reflectx.Mapper @@ -41,7 +40,6 @@ type Iterx struct { func Iter(q *gocql.Query) *Iterx { return &Iterx{ Iter: q.Iter(), - query: q, 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 // can scan into that type. If no rows were selected, ErrNotFound is returned. func (iter *Iterx) Get(dest interface{}) error { - if iter.query == nil { - return errors.New("using released query") - } - iter.scanAny(dest, false) iter.Close() - iter.ReleaseQuery() 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, // then each row must only have one column which can scan into that type. func (iter *Iterx) Select(dest interface{}) error { - if iter.query == nil { - return errors.New("using released query") - } - iter.scanAll(dest, false) iter.Close() - iter.ReleaseQuery() 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 // types. func (iter *Iterx) StructScan(dest interface{}) bool { - if iter.query == nil { - iter.err = errors.New("using released query") - return false - } - v := reflect.ValueOf(dest) if v.Kind() != reflect.Ptr { 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 } -// 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. func (iter *Iterx) checkErrAndNotFound() error { if iter.err != nil {