Repository: gocql/gocql
Pull request #1638 (2022-07-04 13:47:21)

Update CHANGELOG


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82996d9..4a2bccd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,18 +6,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+### Added
+
+### Changed
+
+### Fixed
+
+## [1.2.0] - 2022-07-07
+
 This release improves support for connecting through proxies and some improvements when using Cassandra 4.0 or later.
 
 ### Added
-- HostDialer interface now allows customizing connection including TLS setup per host.
+- HostDialer interface now allows customizing connection including TLS setup per host. (#1629)
 
 ### Changed
-- The driver now uses `host_id` instead of connect address to identify nodes.
+- The driver now uses `host_id` instead of connect address to identify nodes. (#1632)
 - gocql reads `system.peers_v2` instead of `system.peers` when connected to Cassandra 4.0 or later and
-  populates `HostInfo.Port` using the native port.
+  populates `HostInfo.Port` using the native port. (#1635)
 
 ### Fixed
-- Data race in `HostInfo.HostnameAndPort()`.
+- Data race in `HostInfo.HostnameAndPort()`. (#1631)
+- Handling of nils when marshaling/unmarshaling lists and maps. (#1630)
+- Silent data corruption in case a map was serialized into UDT and some fields in the UDT were not present in the map.
+  The driver now correctly writes nulls instead of shifting fields. (#1626, #1639)
 
 ## [1.1.0] - 2022-04-29
 
diff --git a/marshal.go b/marshal.go
index e558b1b..bcb6de5 100644
--- a/marshal.go
+++ b/marshal.go
@@ -2210,15 +2210,13 @@ func marshalUDT(info TypeInfo, value interface{}) ([]byte, error) {
 		var buf []byte
 		for _, e := range udt.Elements {
 			val, ok := v[e.Name]
+			if !ok {
+				return nil, marshalErrorf("marshal missing map key %q", e.Name)
+			}
 
-			var data []byte
-
-			if ok {
-				var err error
-				data, err = Marshal(e.Type, val)
-				if err != nil {
-					return nil, err
-				}
+			data, err := Marshal(e.Type, val)
+			if err != nil {
+				return nil, err
 			}
 
 			buf = appendBytes(buf, data)


Test patch:
diff --git a/marshal_test.go b/marshal_test.go
index 5df7d4b..a67e202 100644
--- a/marshal_test.go
+++ b/marshal_test.go
@@ -2003,29 +2003,9 @@ func TestMarshalUDTMap(t *testing.T) {
 			"y": 2,
 			"z": 3,
 		}
-		expected := []byte("\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03")
-
-		data, err := Marshal(typeInfo, value)
-		if err != nil {
-			t.Errorf("got error %#v", err)
-		}
-		if !bytes.Equal(data, expected) {
-			t.Errorf("got value %x", data)
-		}
-	})
-	t.Run("partially bound from the beginning", func(t *testing.T) {
-		value := map[string]interface{}{
-			"x": 1,
-			"y": 2,
-		}
-		expected := []byte("\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\xff\xff\xff\xff")
-
-		data, err := Marshal(typeInfo, value)
-		if err != nil {
-			t.Errorf("got error %#v", err)
-		}
-		if !bytes.Equal(data, expected) {
-			t.Errorf("got value %x", data)
+		me := MarshalError(`marshal missing map key "x"`)
+		if _, err := Marshal(typeInfo, value); err != me {
+			t.Errorf("got error %#v, want %#v", err, me)
 		}
 	})
 	t.Run("fully bound", func(t *testing.T) {
@@ -2041,76 +2021,7 @@ func TestMarshalUDTMap(t *testing.T) {
 			t.Errorf("got error %#v", err)
 		}
 		if !bytes.Equal(data, expected) {
-			t.Errorf("got value %x", data)
-		}
-	})
-}
-
-func TestMarshalUDTStruct(t *testing.T) {
-	typeInfo := UDTTypeInfo{NativeType{proto: 3, typ: TypeUDT}, "", "xyz", []UDTField{
-		{Name: "x", Type: NativeType{proto: 3, typ: TypeInt}},
-		{Name: "y", Type: NativeType{proto: 3, typ: TypeInt}},
-		{Name: "z", Type: NativeType{proto: 3, typ: TypeInt}},
-	}}
-
-	type xyzStruct struct {
-		X int32 `cql:"x"`
-		Y int32 `cql:"y"`
-		Z int32 `cql:"z"`
-	}
-	type xyStruct struct {
-		X int32 `cql:"x"`
-		Y int32 `cql:"y"`
-	}
-	type yzStruct struct {
-		Y int32 `cql:"y"`
-		Z int32 `cql:"z"`
-	}
-
-	t.Run("partially bound", func(t *testing.T) {
-		value := yzStruct{
-			Y: 2,
-			Z: 3,
-		}
-		expected := []byte("\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03")
-
-		data, err := Marshal(typeInfo, value)
-		if err != nil {
-			t.Errorf("got error %#v", err)
-		}
-		if !bytes.Equal(data, expected) {
-			t.Errorf("got value %x", data)
-		}
-	})
-	t.Run("partially bound from the beginning", func(t *testing.T) {
-		value := xyStruct{
-			X: 1,
-			Y: 2,
-		}
-		expected := []byte("\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\xff\xff\xff\xff")
-
-		data, err := Marshal(typeInfo, value)
-		if err != nil {
-			t.Errorf("got error %#v", err)
-		}
-		if !bytes.Equal(data, expected) {
-			t.Errorf("got value %x", data)
-		}
-	})
-	t.Run("fully bound", func(t *testing.T) {
-		value := xyzStruct{
-			X: 1,
-			Y: 2,
-			Z: 3,
-		}
-		expected := []byte("\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03")
-
-		data, err := Marshal(typeInfo, value)
-		if err != nil {
-			t.Errorf("got error %#v", err)
-		}
-		if !bytes.Equal(data, expected) {
-			t.Errorf("got value %x", data)
+			t.Errorf("got error %x", data)
 		}
 	})
 }