Exception on UDT => CLR mapping of collection property which has null value

Description

Exception on mapping collection in UDT to CLR collection, if value in DB is null

Example:
DB
CREATE TYPE IF NOT EXISTS settings (
ids frozen<list<int>>
);

CREATE TABLE IF NOT EXISTS test_users (
user_id int,
settings frozen<settings>,
PRIMARY KEY (user_id)
);

INSERT INTO test_users (user_id, settings) VALUES (1, { "ids": null });
SELECT * FROM test_users;

user_id | languages
--------+------------
1 | {ids: null}

C# Code to reproduce the bug:

using System;
using System.Collections.Generic;
using System.Linq;
using Cassandra;

namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
var cluster = Cluster.Builder()
.WithConnectionString("Contact Points=127.0.0.1;Port=9042")
.Build();

var connection = cluster.ConnectAsync().GetAwaiter().GetResult();
connection.UserDefinedTypes.Define(UdtMap.For<Settings>("settings", "users").Map(clr => clr.Ids, "ids"));
var rowSet = connection.Execute("SELECT settings FROM users.test_users WHERE user_id=1");
var user = rowSet.First();
var settings = user.GetValue<Settings>("settings");

Console.WriteLine($"Settings: {settings.Ids}");
Console.ReadLine();
}
}

public class Settings
{
public IList<int> Ids { get; set; }
}
}

--------------------------------------
An unhandled exception has occurred while executing the request.
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentNullException: Value cannot be null.
Parameter name: collection
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at Cassandra.Mapping.TypeConversion.TypeConverter.ConvertToList[T](IEnumerable`1 list)
— End of inner exception stack trace —
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Delegate.DynamicInvokeImpl(Object[] args)
at Cassandra.Mapping.TypeConversion.TypeConverter.ConvertToUdtFieldFromDbValue(Type dbType, Type valueType, Object value)
at Cassandra.UdtMap.ToObject(Object[] values)
at Cassandra.Serialization.UdtSerializer.Deserialize(UInt16 protocolVersion, Byte[] buffer, Int32 offset, Int32 length, IColumnInfo typeInfo)
at Cassandra.Serialization.Serializer.Deserialize(Byte[] buffer, Int32 offset, Int32 length, ColumnTypeCode typeCode, IColumnInfo typeInfo)
at Cassandra.Serialization.CollectionSerializer.Deserialize(UInt16 protocolVersion, Byte[] buffer, Int32 offset, Int32 length, IColumnInfo typeInfo)
at Cassandra.Serialization.Serializer.Deserialize(Byte[] buffer, Int32 offset, Int32 length, ColumnTypeCode typeCode, IColumnInfo typeInfo)
at Cassandra.FrameReader.ReadFromBytes(Byte[] buffer, Int32 offset, Int32 length, ColumnTypeCode typeCode, IColumnInfo typeInfo)
at Cassandra.OutputRows.ProcessRowItem(FrameReader reader)
at Cassandra.OutputRows.ProcessRows(RowSet rs, FrameReader reader)
at Cassandra.OutputRows..ctor(FrameReader reader, Nullable`1 traceId)
at Cassandra.Responses.ResultResponse..ctor(Frame frame)
at Cassandra.Responses.ResultResponse.Create(Frame frame)
at Cassandra.FrameParser.Parse(Frame frame)

int[] failures like the IList but with a bit different exception
{System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentNullException: Value cannot be null.
Parameter name: source
at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable`1 source, Func`2 selector)
at Cassandra.Mapping.TypeConversion.TypeConverter.ConvertToArrayFromDb[TSource,TResult](IEnumerable`1 listFromDatabase)

IEnumerable<int> does not fail in case of null, but does not map in case of value, but it's a different story.

Why, I believe, the issue is blocker:
1. The same driver enables writing null in collection inside UDT;
2. Once null has been written, it cannot be read again. Besides, system failures on an attempt of reading such data. That's critical. Fortunately, I noticed such behavior before going into production. If it had been revealed in prod, it would be a catastrophe.
3. I haven't found a workaround: no hooks in mapping pipeline to inject some fixes into; no alternatives.

Environment

None

Pull Requests

None

Status

Assignee

Unassigned

Reporter

Andrey Braynin

Labels

Reproduced in

3.11.0

PM Priority

None

Fix versions

None

External issue ID

None

Doc Impact

None

Reviewer

None

Pull Request

None

Epic Link

None

Sprint

Size

None

Components

Affects versions

Priority

Blocker
Configure