spring-projects / spring-data-cassandra

Provides support to increase developer productivity in Java when using Apache Cassandra. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
https://spring.io/projects/spring-data-cassandra/
Apache License 2.0
374 stars 310 forks source link

Querying `BIGINT` field with `Integer` value fails with `CodecNotFoundException` #1383

Closed mpashka closed 1 year ago

mpashka commented 1 year ago

I'm trying to update cassandra udf field using CassandraOperations update API.

        cql.update(Query.query(
                Criteria.where(Attachment.Key.MESSAGE_ID).is(5),
                Criteria.where(Attachment.Key.ATTACHMENT_ID).is(6)
        ), Update.update(Attachment.DATA, Attachment.AttachmentData.builder()
                .status("Hello4")
                .build()
        ), Attachment.class);

And that results in error:

com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException: Codec not found for requested operation: [BIGINT <-> java.lang.Integer]

After adding appropriate codec manually (long <-> BIGINT with java.lang.Integer java type exposed) that code works fine:

        ((MutableCodecRegistry) cql.getConverter().getCodecRegistry()).register(new BigintIntegerCodec());

where BigintIntegerCodec is copy-paste from com.datastax.oss.driver.internal.core.type.codec.BigIntCodec class with java.lang.Integer instead of java.lang.Long java type exposed.

Cassandra schema:

> DESCRIBE TABLE geps.attachment;

CREATE TABLE geps.attachment (
    message_id bigint,
    attachment_id bigint,
    parent_id bigint,
    data attachment_data,
    signature attachment_signature,
    PRIMARY KEY (message_id, attachment_id)
) WITH CLUSTERING ORDER BY (attachment_id ASC)
    AND additional_write_policy = '99p'
    AND bloom_filter_fp_chance = 0.1
    AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'}
    AND cdc = false
    AND comment = 'Данные вложений сообщений и eds-подписей'
    AND compaction = {'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4', 'sstable_size_in_mb': '160'}
    AND compression = {'chunk_length_in_kb': '16', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor'}
    AND memtable = 'default'
    AND crc_check_chance = 1.0
    AND default_time_to_live = 0
    AND extensions = {}
    AND gc_grace_seconds = 864000
    AND max_index_interval = 2048
    AND memtable_flush_period_in_ms = 0
    AND min_index_interval = 128
    AND read_repair = 'BLOCKING'
    AND speculative_retry = '99p';

CREATE INDEX attachment_by_id ON geps.attachment (attachment_id);

CREATE INDEX attachment_by_parent_id ON geps.attachment (parent_id);

> DESCRIBE TYPE geps.attachment_data;

CREATE TYPE geps.attachment_data (
    file_name text,
    file_size bigint,
    mime_type text,
    spf_blank_id text,
    status ascii,
    external_link text,
    create_date timestamp,
    update_date timestamp,
    base_dir_id smallint
);

Here is junit test

mp911de commented 1 year ago

I'm not quite sure what you're asking for. If you provide a different data type to either Cassandra or the prepared statement, then your query will fail. Codecs are a matter of the Cassandra driver, Spring Data only uses the driver and the codecs.

A more lenient approach of the driver, by accepting Integer for BIGINT would be neat. However, please reach out to the Cassandra driver team for such requests.

mpashka commented 1 year ago

Еhe collective mind from stackoverflow gives an advice to remove @CassandraType annotations. That didn't help in my case. Probably column type update in cassandra can help.

For now I don't see possibility to update nested (udt) field value in cassandra through spring data query API:

Updating separate field results in Undefined column name:

        cql.update(Query.query(
                Criteria.where(Attachment.Key.MESSAGE_ID).is(5),
                Criteria.where(Attachment.Key.ATTACHMENT_ID).is(6)
        ), Update.update(Attachment.DATA + '.' + Attachment.AttachmentData.DT_STATUS, "Hello5"), Attachment.class);

org.springframework.data.cassandra.CassandraInvalidQueryException: Query; CQL [UPDATE attachment SET status=? WHERE message_id=? AND attachment_id=?]; Unknown identifier status; nested exception is com.datastax.oss.driver.api.core.servererrors.InvalidQueryException: Unknown identifier status
...
Caused by: com.datastax.oss.driver.api.core.servererrors.InvalidQueryException: Unknown identifier status

while I'm using 'data.status' for column name. And I didn't find an option to fix that.

Updating whole object:

        cql.update(Query.query(
                Criteria.where(Attachment.Key.MESSAGE_ID).is(5),
                Criteria.where(Attachment.Key.ATTACHMENT_ID).is(6)
        ), Update.update(Attachment.DATA, Attachment.AttachmentData.builder()
                .status("Hello4")
                .build()
        ), Attachment.class);

reports com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException: Codec not found for requested operation: [BIGINT <-> java.lang.Integer]

I suppose that is a defect, but probably I'm wrong and that's ok.

mp911de commented 1 year ago

If you change .is(5) to .is(5L) (using long values), then the issue goes away. Same for the attachment Id.