瀏覽代碼

Merge pull request #563 from Zariel/tuple-nested-types

correctly handle tuples in MapScan
Chris Bannister 10 年之前
父節點
當前提交
bdffb5ef46
共有 5 個文件被更改,包括 61 次插入6 次删除
  1. 2 2
      cassandra_test.go
  2. 22 3
      helpers.go
  3. 8 0
      marshal.go
  4. 1 1
      session.go
  5. 28 0
      tuple_test.go

+ 2 - 2
cassandra_test.go

@@ -336,8 +336,8 @@ func TestCAS(t *testing.T) {
 	}
 
 	if _, err := session.Query(`DELETE FROM cas_table WHERE title = ? and revid = ? IF last_modified = ?`,
-		title, revid, tenSecondsLater).ScanCAS(); err.Error() != "count mismatch" {
-		t.Fatalf("delete: was expecting count mismatch error but got %s", err)
+		title, revid, tenSecondsLater).ScanCAS(); !strings.HasPrefix(err.Error(), "gocql: not enough columns to scan into") {
+		t.Fatal("delete: was expecting count mismatch error but got: %q", err.Error())
 	}
 
 	if applied, err := session.Query(`DELETE FROM cas_table WHERE title = ? and revid = ? IF last_modified = ?`,

+ 22 - 3
helpers.go

@@ -5,6 +5,7 @@
 package gocql
 
 import (
+	"fmt"
 	"math/big"
 	"reflect"
 	"strings"
@@ -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,

+ 8 - 0
marshal.go

@@ -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
@@ -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{}
 

+ 1 - 1
session.go

@@ -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
 	}
 

+ 28 - 0
tuple_test.go

@@ -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)
+	}
+}