Dmitry Konstantinov created CASSANDRA-19814:
-----------------------------------------------

             Summary: UDT codec decode is too restrictive in decoding of 
unknown fields
                 Key: CASSANDRA-19814
                 URL: https://issues.apache.org/jira/browse/CASSANDRA-19814
             Project: Cassandra
          Issue Type: Bug
          Components: Client/java-driver
            Reporter: Dmitry Konstantinov
            Assignee: Dmitry Konstantinov


The current UDT code logic in driver 4.x expects the number of UDT fields 
exactly equal to the number of fields in the schema version used to create the 
codec.

As a result it makes applications less friendly to a live upgrade logic when we 
change DB schema and do a rolling restart of applications to switch from an old 
app version to a new one. In this scenario we can add a new field to UDT 
(backward compatible change) and a new application version can write this UDT 
with a new field filled but the old application version will fail to decode 
such UDT with the extra unknown field.

Driver 3.x UDT codec has a different behaviour - the UDT codec logic reads 
known UDT fields received from network and ignores the unknown tail, which 
makes in tolerant to such schema changes:  
https://github.com/apache/cassandra-java-driver/blob/3.x/driver-core/src/main/java/com/datastax/driver/core/TypeCodec.java#L2202

A code example to illustrate the issue:

Schema
{code:java}
CREATE TYPE test_keyspace.basicInfoInner (
  weight text,
  height text
);

CREATE TYPE test_keyspace.basicInfo (
  birthday    timestamp,
  nationality text,
  inner       frozen<basicInfoInner>
);

CREATE TABLE test_keyspace.tableWithUdt
(
    id    varchar,
    value frozen<list<basicInfo>>,
    PRIMARY KEY (id)
); {code}
Code:
{code:java}
CqlSession session = getSession();
UdtCodec udtCodec = new UdtCodec(userDefinedType);
TypeCodec<List<UdtValue>> listOfUdtCodec = TypeCodecs.listOf(udtCodec);

{
    ResultSet resultSet = session.execute("select id, value from tableWithUdt");
    for (Row row : resultSet) {
        String id = row.getString(0);
        List<UdtValue> udtValueList = row.get(1, listOfUdtCodec);
    }
}

session.execute("ALTER TYPE basicInfo ADD details text"); 

session.execute("INSERT INTO tableWithUdt (id, value) VALUES ('4', [\n" +
        " { birthday : '1993-06-18', " +
        "   nationality : 'New Zealand', " +
        "   inner: { weight : '70', height : '70' } " +
        " },\n" +
        " { birthday : '1993-06-19', " +
        "   nationality : null, " +
        "   inner: { weight : '70', height : null }, " +
        "   details: 'added details'" + // <=== insert a value for the new 
added UDT field
        " }\n" +
        "  ])");

{
    ResultSet resultSet = session.execute("select id, value from tableWithUdt");
    for (Row row : resultSet) {
        String id = row.getString(0);
        //  ==== UdtCodec throws IllegalArgumentException: Too many fields in 
encoded UDT value, expected 3 ==== 
        List<UdtValue> udtValueList = row.get(1, listOfUdtCodec);
    }
}

private static UserDefinedType getUserDefinedType(CqlSession session, String 
udtName) {
    return session.getMetadata()
            .getKeyspace(session.getKeyspace().orElseThrow(() -> new 
IllegalArgumentException("Udt name " + udtName + " can't be loaded due to null 
keyspace")))
            .orElseThrow(() -> new IllegalArgumentException("Metadata is not 
found"))
            .getUserDefinedType(udtName)
            .orElseThrow(() -> new IllegalArgumentException("Udt name " + 
udtName + " isn't found"));
}
{code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to