Skip to content

Commit 4c1827e

Browse files
committed
Encode nil map value for UDTs as NULL
Previously, the driver was encoding nil map values for UDT-typed columns as an initialized map with zero values for each UDT field. This patch changes its behaviour to encode nil maps as NULL values in Cassandra. Patch by Bohdan Siryk; reviewed by TBD for CASSGO-118
1 parent c65c762 commit 4c1827e

3 files changed

Lines changed: 109 additions & 0 deletions

File tree

cassandra_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,55 @@ func TestObserve(t *testing.T) {
256256
}
257257
}
258258

259+
func TestObserve_KeyspaceOverrideAPI(t *testing.T) {
260+
session := createSession(t)
261+
defer session.Close()
262+
263+
if err := createTable(session, `CREATE KEYSPACE IF NOT EXISTS gocql_test2 WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }`); err != nil {
264+
t.Fatal("create:", err)
265+
}
266+
267+
if err := createTable(session, `CREATE TABLE IF NOT EXISTS gocql_test2.observe (id int, PRIMARY KEY (id))`); err != nil {
268+
t.Fatal("create:", err)
269+
}
270+
271+
var observedKeyspace string
272+
273+
resetObserved := func() {
274+
observedKeyspace = ""
275+
}
276+
277+
observer := funcQueryObserver(func(ctx context.Context, o ObservedQuery) {
278+
observedKeyspace = o.Keyspace
279+
})
280+
281+
const overrideKeyspace = "gocql_test2"
282+
283+
// Session default keyspace
284+
err := session.Query(`INSERT INTO observe (id) VALUES (?)`, 42).
285+
Observer(observer).
286+
SetKeyspace(overrideKeyspace).
287+
Exec()
288+
if err != nil {
289+
t.Fatal("insert:", err)
290+
}
291+
if observedKeyspace != overrideKeyspace {
292+
t.Fatalf("expected keyspace '%s', got %s", overrideKeyspace, observedKeyspace)
293+
}
294+
295+
resetObserved()
296+
var id int
297+
if err := session.Query(`SELECT id FROM observe WHERE id = ?`, 42).
298+
Observer(observer).
299+
SetKeyspace(overrideKeyspace).
300+
Scan(&id); err != nil {
301+
t.Fatal("select:", err)
302+
}
303+
if observedKeyspace != overrideKeyspace {
304+
t.Fatalf("expected keyspace '%s', got %s", overrideKeyspace, observedKeyspace)
305+
}
306+
}
307+
259308
func TestObserve_Pagination(t *testing.T) {
260309
session := createSession(t)
261310
defer session.Close()
@@ -4676,3 +4725,43 @@ func TestIterMapScanUDT(t *testing.T) {
46764725
require.NoError(t, err)
46774726
require.Nil(t, rawResult["value"])
46784727
}
4728+
4729+
func TestUDT_MarshalNilMap(t *testing.T) {
4730+
session := createSession(t)
4731+
defer session.Close()
4732+
4733+
err := createTable(session, `CREATE TYPE IF NOT EXISTS gocql_test.nil_map_udt (
4734+
field_a text,
4735+
field_b int
4736+
);`)
4737+
require.NoError(t, err)
4738+
4739+
err = createTable(session, `CREATE TABLE IF NOT EXISTS gocql_test.nil_map_udt_table (
4740+
id int PRIMARY KEY,
4741+
value frozen<nil_map_udt>
4742+
);`)
4743+
require.NoError(t, err)
4744+
4745+
nilMap := map[string]interface{}(nil)
4746+
err = session.Query("INSERT INTO nil_map_udt_table (id, value) VALUES (?, ?)", 1, nilMap).Exec()
4747+
require.NoError(t, err)
4748+
4749+
ctx := context.Background()
4750+
4751+
const selectQuery = "SELECT value FROM nil_map_udt_table WHERE id = ?"
4752+
t.Run("scan", func(t *testing.T) {
4753+
var scanned map[string]interface{}
4754+
err = session.Query(selectQuery, 1).ScanContext(ctx, &scanned)
4755+
require.NoError(t, err)
4756+
require.Nil(t, scanned)
4757+
})
4758+
4759+
t.Run("iter map scan", func(t *testing.T) {
4760+
rawResult := map[string]interface{}{}
4761+
rawResultIter := session.Query(selectQuery, 1).IterContext(ctx)
4762+
rawResultIter.MapScan(rawResult)
4763+
err = rawResultIter.Close()
4764+
require.NoError(t, err)
4765+
require.Nil(t, rawResult["value"])
4766+
})
4767+
}

marshal.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,6 +2855,12 @@ func (udt UDTTypeInfo) Marshal(value interface{}) ([]byte, error) {
28552855

28562856
return buf, nil
28572857
case map[string]interface{}:
2858+
if v == nil {
2859+
// If the map is nil, we should marshal it as NULL value.
2860+
// framer will encode this as NULL value
2861+
return nil, nil
2862+
}
2863+
28582864
var buf []byte
28592865
for i := range udt.Elements {
28602866
val, ok := v[udt.Elements[i].Name]

marshal_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2131,6 +2131,20 @@ func TestMarshalUDTMap(t *testing.T) {
21312131
},
21322132
}
21332133

2134+
t.Run("nil map", func(t *testing.T) {
2135+
value := map[string]interface{}(nil)
2136+
expected := []byte(nil)
2137+
2138+
data, err := Marshal(typeInfo, value)
2139+
if err != nil {
2140+
t.Errorf("got error %#v", err)
2141+
}
2142+
2143+
if !reflect.DeepEqual(data, expected) {
2144+
t.Errorf("got value %#v", data)
2145+
}
2146+
})
2147+
21342148
t.Run("partially bound", func(t *testing.T) {
21352149
value := map[string]interface{}{
21362150
"y": 2,

0 commit comments

Comments
 (0)