Skip to content

Commit

Permalink
correctly handle tuples in MapScan
Browse files Browse the repository at this point in the history
Tuples are handled specially in Scan and as such each element in the
tuple must be treated as a single column. MapScan now generates one
element in the map for each element in the tuple, these can be accessed
by name using the new TupleColumnName function.

fixes apache#561
  • Loading branch information
Zariel committed Jan 10, 2016
1 parent 57edf1e commit c711a53
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 4 deletions.
25 changes: 22 additions & 3 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package gocql

import (
"fmt"
"math/big"
"reflect"
"strings"
Expand Down Expand Up @@ -128,16 +129,34 @@ func (r *RowData) rowMap(m map[string]interface{}) {
}
}

// TupeColumnName will return the column name of a tuple value in a column named
// c at index n. It should be used if a specific element within a tuple is needed
// to be extracted from a map returned from SliceMap or MapScan.
func TupleColumnName(c string, n int) string {
return fmt.Sprintf("%s[%d]", c, n)
}

func (iter *Iter) RowData() (RowData, error) {
if iter.err != nil {
return RowData{}, iter.err
}

columns := make([]string, 0)
values := make([]interface{}, 0)

for _, column := range iter.Columns() {
val := column.TypeInfo.New()
columns = append(columns, column.Name)
values = append(values, val)

switch c := column.TypeInfo.(type) {
case TupleTypeInfo:
for i, elem := range c.Elems {
columns = append(columns, TupleColumnName(column.Name, i))
values = append(values, elem.New())
}
default:
val := column.TypeInfo.New()
columns = append(columns, column.Name)
values = append(values, val)
}
}
rowData := RowData{
Columns: columns,
Expand Down
8 changes: 8 additions & 0 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,10 @@ type TupleTypeInfo struct {
Elems []TypeInfo
}

func (t TupleTypeInfo) New() interface{} {
return reflect.New(goType(t)).Interface()
}

type UDTField struct {
Name string
Type TypeInfo
Expand All @@ -1628,6 +1632,10 @@ type UDTTypeInfo struct {
Elements []UDTField
}

func (u UDTTypeInfo) New() interface{} {
return reflect.New(goType(u)).Interface()
}

func (u UDTTypeInfo) String() string {
buf := &bytes.Buffer{}

Expand Down
2 changes: 1 addition & 1 deletion session.go
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ func (iter *Iter) Scan(dest ...interface{}) bool {
// currently only support scanning into an expand tuple, such that its the same
// as scanning in more values from a single column
if len(dest) != iter.meta.actualColCount {
iter.err = errors.New("count mismatch")
iter.err = fmt.Errorf("gocql: not enough columns to scan into: have %d want %d", len(dest), iter.meta.actualColCount)
return false
}

Expand Down
28 changes: 28 additions & 0 deletions tuple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,31 @@ func TestTupleSimple(t *testing.T) {
t.Errorf("expected to get coord.y=-100 got: %v", coord.y)
}
}

func TestTupleMapScan(t *testing.T) {
if *flagProto < protoVersion3 {
t.Skip("tuple types are only available of proto>=3")
}

session := createSession(t)
defer session.Close()

err := createTable(session, `CREATE TABLE gocql_test.tuple_map_scan(
id int,
val frozen<tuple<int, int>>,
primary key(id))`)
if err != nil {
t.Fatal(err)
}

if err := session.Query(`INSERT INTO tuple_map_scan (id, val) VALUES (1, (1, 2));`).Exec(); err != nil {
t.Fatal(err)
}

m := make(map[string]interface{})
err = session.Query(`SELECT * FROM tuple_map_scan`).MapScan(m)
if err != nil {
t.Fatal(err)
}
}

0 comments on commit c711a53

Please sign in to comment.