diff --git a/README.md b/README.md index 568e2e4..005abb9 100644 --- a/README.md +++ b/README.md @@ -1 +1,21 @@ # gocqlx [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/mmatczuk/gocqlx) [![Go Report Card](https://goreportcard.com/badge/github.com/mmatczuk/gocqlx)](https://goreportcard.com/report/github.com/mmatczuk/gocqlx) [![Build Status](http://img.shields.io/travis/mmatczuk/gocqlx.svg?style=flat-square)](https://travis-ci.org/mmatczuk/gocqlx) + +Package `gocqlx` is a `gocql` extension, similar to what `sqlx` is to `database/sql`. + +It provides a new type that seamlessly wraps `gocql.Iter` and provide +convenience methods which are useful in the development of database driven +applications. None of the underlying gocql.Iter methods are changed. +Instead all extended behavior is implemented through new methods defined on +wrapper type. + +The wrapper type enables you to bind iterator row into a struct. Under the +hood it uses `sqlx/reflectx` package, models / structs working whit `sqlx` will +also work with `gocqlx`. + +## Installation + + go get github.com/mmatczuk/gocqlx + +## Example + +See [example test](https://github.com/mmatczuk/gocqlx/blob/master/example_test.go). diff --git a/doc.go b/doc.go index 51483c3..fc07604 100644 --- a/doc.go +++ b/doc.go @@ -13,7 +13,14 @@ // Example, read all items for a given id // // var v []*Item -// if err := gocqlx.Select(session.Query(`SELECT * FROM items WHERE id = ?`, id), &v); err != nil { +// if err := gocqlx.Select(&v, session.Query(`SELECT * FROM items WHERE id = ?`, id)); err != nil { // log.Fatal("select failed", err) // } +// +// Example, read first item for a given id +// +// var v Item +// if err := gocqlx.Get(&v, session.Query(`SELECT * FROM items WHERE id = ?`, id)); err != nil { +// log.Fatal("get failed", err) +// } package gocqlx diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..01f10b4 --- /dev/null +++ b/example_test.go @@ -0,0 +1,100 @@ +// +build all integration + +package gocqlx + +import ( + "fmt" + "testing" + + "github.com/gocql/gocql" + "github.com/mmatczuk/gocqlx" +) + +var personSchema = ` +CREATE TABLE gocqlx_test.person ( + first_name text, + last_name text, + email list, + PRIMARY KEY(first_name, last_name) +)` + +var placeSchema = ` +CREATE TABLE gocqlx_test.place ( + country text, + city text, + telcode int, + PRIMARY KEY(country, city) +)` + +type Person struct { + FirstName string `db:"first_name"` + LastName string `db:"last_name"` + Email []string +} + +type Place struct { + Country string + City string + TelCode int +} + +func TestExample(t *testing.T) { + session := createSession(t) + defer session.Close() + + // Exec the schema or fail + mustExec := func(q *gocql.Query) { + if err := q.Exec(); err != nil { + t.Fatal("insert:", q, err) + } + } + mustExec(session.Query(personSchema)) + mustExec(session.Query(placeSchema)) + + mustExec(session.Query("INSERT INTO gocqlx_test.person (first_name, last_name, email) VALUES (?, ?, ?)", "Jason", "Moiron", []string{"jmoiron@jmoiron.net"})) + mustExec(session.Query("INSERT INTO gocqlx_test.person (first_name, last_name, email) VALUES (?, ?, ?)", "John", "Doe", []string{"johndoeDNE@gmail.net"})) + mustExec(session.Query("INSERT INTO gocqlx_test.place (country, city, telcode) VALUES (?, ?, ?)", "United States", "New York", 1)) + mustExec(session.Query("INSERT INTO gocqlx_test.place (country, city, telcode) VALUES (?, ?, ?)", "Hong Kong", "", 852)) + mustExec(session.Query("INSERT INTO gocqlx_test.place (country, city, telcode) VALUES (?, ?, ?)", "Singapore", "", 65)) + + // TODO + // tx.NamedExec("INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)", &Person{"Jane", "Citizen", "jane.citzen@gocqlx_test.com"}) + + // Query the database, storing results in a []Person (wrapped in []interface{}) + { + people := []Person{} + if err := gocqlx.Select(&people, session.Query("SELECT * FROM person")); err != nil { + t.Fatal("select:", err) + } + + fmt.Printf("%#v\n%#v\n", people[0], people[1]) + // gocqlx_test.Person{FirstName:"John", LastName:"Doe", Email:[]string{"johndoeDNE@gmail.net"}} + // gocqlx_test.Person{FirstName:"Jason", LastName:"Moiron", Email:[]string{"jmoiron@jmoiron.net"}} + } + + // Get a single result, a la QueryRow + { + var jason Person + if err := gocqlx.Get(&jason, session.Query("SELECT * FROM person WHERE first_name=?", "Jason")); err != nil { + t.Fatal("get:", err) + } + fmt.Printf("%#v\n", jason) + // gocqlx_test.Person{FirstName:"Jason", LastName:"Moiron", Email:[]string{"jmoiron@jmoiron.net"}} + } + + // Loop through rows using only one struct + { + var place Place + iter := gocqlx.Iter(session.Query("SELECT * FROM place")) + for iter.StructScan(&place) { + fmt.Printf("%#v\n", place) + } + iter.Close() + if err := iter.Err(); err != nil { + t.Fatal("iter:", err) + } + // gocqlx_test.Place{Country:"Hong Kong", City:"", TelCode:852} + // gocqlx_test.Place{Country:"United States", City:"New York", TelCode:1} + // gocqlx_test.Place{Country:"Singapore", City:"", TelCode:65} + } +} diff --git a/gocqlx.go b/gocqlx.go index b3bb9f1..9bc26e1 100644 --- a/gocqlx.go +++ b/gocqlx.go @@ -17,12 +17,12 @@ import ( var DefaultMapper = reflectx.NewMapperFunc("db", strings.ToLower) // Get is a convenience function for creating iterator and calling Get on it. -func Get(q *gocql.Query, dest interface{}) error { +func Get(dest interface{}, q *gocql.Query) error { return Iter(q).Get(dest) } // Select is a convenience function for creating iterator and calling Select on it. -func Select(q *gocql.Query, dest interface{}) error { +func Select(dest interface{}, q *gocql.Query) error { return Iter(q).Select(dest) } @@ -55,9 +55,7 @@ func (iter *Iterx) Get(dest interface{}) error { iter.err = err } - if err := iter.Close(); err != nil { - iter.err = err - } + iter.Close() return iter.err } @@ -100,9 +98,7 @@ func (iter *Iterx) Select(dest interface{}) error { iter.err = err } - if err := iter.Close(); err != nil { - iter.err = err - } + iter.Close() return iter.err } @@ -218,6 +214,16 @@ func columnNames(ci []gocql.ColumnInfo) []string { return r } +// Close closes the iterator and returns any errors that happened during +// the query or the iteration. +func (iter *Iterx) Close() error { + err := iter.Iter.Close() + if err != nil && iter.err == nil { + iter.err = err + } + return err +} + // Err returns the error encountered while scanning. func (iter *Iterx) Err() error { return iter.err diff --git a/integration_test.go b/integration_test.go index 6890536..126581f 100644 --- a/integration_test.go +++ b/integration_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/gocql/gocql" + "github.com/mmatczuk/gocqlx" "gopkg.in/inf.v0" ) @@ -43,7 +44,7 @@ func TestScannable(t *testing.T) { t.Run("get", func(t *testing.T) { var v FullName - if err := Get(session.Query(`SELECT testfullname FROM scannable_table`), &v); err != nil { + if err := gocqlx.Get(&v, session.Query(`SELECT testfullname FROM scannable_table`)); err != nil { t.Fatal("get failed", err) } @@ -54,7 +55,7 @@ func TestScannable(t *testing.T) { t.Run("select", func(t *testing.T) { var v []FullName - if err := Select(session.Query(`SELECT testfullname FROM scannable_table`), &v); err != nil { + if err := gocqlx.Select(&v, session.Query(`SELECT testfullname FROM scannable_table`)); err != nil { t.Fatal("get failed", err) } @@ -69,7 +70,7 @@ func TestScannable(t *testing.T) { t.Run("select ptr", func(t *testing.T) { var v []*FullName - if err := Select(session.Query(`SELECT testfullname FROM scannable_table`), &v); err != nil { + if err := gocqlx.Select(&v, session.Query(`SELECT testfullname FROM scannable_table`)); err != nil { t.Fatal("get failed", err) } @@ -173,7 +174,7 @@ func TestStruct(t *testing.T) { t.Run("get", func(t *testing.T) { var v StructTable - if err := Get(session.Query(`SELECT * FROM struct_table`), &v); err != nil { + if err := gocqlx.Get(&v, session.Query(`SELECT * FROM struct_table`)); err != nil { t.Fatal("get failed", err) } @@ -184,7 +185,7 @@ func TestStruct(t *testing.T) { t.Run("select", func(t *testing.T) { var v []StructTable - if err := Select(session.Query(`SELECT * FROM struct_table`), &v); err != nil { + if err := gocqlx.Select(&v, session.Query(`SELECT * FROM struct_table`)); err != nil { t.Fatal("select failed", err) } @@ -199,7 +200,7 @@ func TestStruct(t *testing.T) { t.Run("select ptr", func(t *testing.T) { var v []*StructTable - if err := Select(session.Query(`SELECT * FROM struct_table`), &v); err != nil { + if err := gocqlx.Select(&v, session.Query(`SELECT * FROM struct_table`)); err != nil { t.Fatal("select failed", err) }