Skip to content

Commit

Permalink
tuples: correct unmarshalling of unset tuple values
Browse files Browse the repository at this point in the history
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: apache#1343
  • Loading branch information
dahankzter committed Apr 2, 2020
1 parent 6d895e3 commit 3cf2db8
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 2 deletions.
5 changes: 3 additions & 2 deletions marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
107 changes: 107 additions & 0 deletions tuple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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<tuple<int, int>>,
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()
Expand All @@ -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<tuple<int, int>>,
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<tuple<int, int>>,
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) {
Expand Down

0 comments on commit 3cf2db8

Please sign in to comment.