From d64139de36abb31cec294e5af4e83a7a2893d4d7 Mon Sep 17 00:00:00 2001 From: Henrik Johansson Date: Thu, 2 Apr 2020 11:35:18 +0200 Subject: [PATCH] tuples: correct unmarshalling of unset tuple values In case the tuple value has never been written the unmarshalling used to panic. This is now handled by inspecting the data from the database and if it is missing (nil) it will set the tuple value to the corresponding default value. Fixes: #1343 --- marshal.go | 5 ++- tuple_test.go | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/marshal.go b/marshal.go index 0592457fc..fdc6a6b0c 100644 --- a/marshal.go +++ b/marshal.go @@ -1914,8 +1914,9 @@ func unmarshalTuple(info TypeInfo, data []byte, value interface{}) error { for i, elem := range tuple.Elems { // each element inside data is a [bytes] var p []byte - p, data = readBytes(data) - + if len(data) > 4 { + p, data = readBytes(data) + } err := Unmarshal(elem, p, v[i]) if err != nil { return err diff --git a/tuple_test.go b/tuple_test.go index 9a6928875..06117c1f0 100644 --- a/tuple_test.go +++ b/tuple_test.go @@ -89,6 +89,45 @@ func TestTuple_NullTuple(t *testing.T) { } +func TestTuple_TupleNotSet(t *testing.T) { + session := createSession(t) + defer session.Close() + if session.cfg.ProtoVersion < protoVersion3 { + t.Skip("tuple types are only available of proto>=3") + } + + err := createTable(session, `CREATE TABLE gocql_test.tuple_not_set_test( + id int, + coord frozen>, + + primary key(id))`) + if err != nil { + t.Fatal(err) + } + + const id = 1 + + err = session.Query("INSERT INTO tuple_not_set_test(id) VALUES(?)", id).Exec() + if err != nil { + t.Fatal(err) + } + + x := new(int) + y := new(int) + iter := session.Query("SELECT coord FROM tuple_not_set_test WHERE id=?", id) + if err := iter.Scan(&x, &y); err != nil { + t.Fatal(err) + } + + if x != nil { + t.Fatalf("x should be nil got %+#v, value=%d", x, *x) + } + if y != nil { + t.Fatalf("y should be nil got %+#v, value=%d", y, *y) + } + +} + func TestTupleMapScan(t *testing.T) { session := createSession(t) defer session.Close() @@ -114,6 +153,74 @@ func TestTupleMapScan(t *testing.T) { if err != nil { t.Fatal(err) } + if m["val[0]"] != 1 { + t.Fatalf("expacted val[0] to be %d but was %d", 1, m["val[0]"]) + } + if m["val[1]"] != 2 { + t.Fatalf("expacted val[1] to be %d but was %d", 2, m["val[1]"]) + } +} + +func TestTupleMapScanNil(t *testing.T) { + session := createSession(t) + defer session.Close() + if session.cfg.ProtoVersion < protoVersion3 { + t.Skip("tuple types are only available of proto>=3") + } + err := createTable(session, `CREATE TABLE gocql_test.tuple_map_scan_nil( + id int, + val frozen>, + + primary key(id))`) + if err != nil { + t.Fatal(err) + } + if err := session.Query(`INSERT INTO tuple_map_scan_nil (id, val) VALUES (?,(?,?));`, 1, nil, nil).Exec(); err != nil { + t.Fatal(err) + } + + m := make(map[string]interface{}) + err = session.Query(`SELECT * FROM tuple_map_scan_nil`).MapScan(m) + if err != nil { + t.Fatal(err) + } + if m["val[0]"] != 0 { + t.Fatalf("expacted val[0] to be %d but was %d", 0, m["val[0]"]) + } + if m["val[1]"] != 0 { + t.Fatalf("expacted val[1] to be %d but was %d", 0, m["val[1]"]) + } +} + +func TestTupleMapScanNotSet(t *testing.T) { + session := createSession(t) + defer session.Close() + if session.cfg.ProtoVersion < protoVersion3 { + t.Skip("tuple types are only available of proto>=3") + } + err := createTable(session, `CREATE TABLE gocql_test.tuple_map_scan_not_set( + id int, + val frozen>, + + primary key(id))`) + if err != nil { + t.Fatal(err) + } + if err := session.Query(`INSERT INTO tuple_map_scan_not_set (id) VALUES (?);`, 1).Exec(); err != nil { + t.Fatal(err) + } + + m := make(map[string]interface{}) + err = session.Query(`SELECT * FROM tuple_map_scan_not_set`).MapScan(m) + if err != nil { + t.Fatal(err) + } + if m["val[0]"] != 0 { + t.Fatalf("expacted val[0] to be %d but was %d", 0, m["val[0]"]) + } + if m["val[1]"] != 0 { + t.Fatalf("expacted val[1] to be %d but was %d", 0, m["val[1]"]) + } } func TestTuple_NestedCollection(t *testing.T) {