Skip to content

Commit

Permalink
decoder: handle empty non-string types
Browse files Browse the repository at this point in the history
For legacy reasons, non-string types support "empty" values, e.g. you can have
an empty int, distinct from NULL. This case used to trigger deserialization errors,
so now it's simply a fallback to NULL. Another option is to introduce a new "empty"
state that a value can be in, but it's semantically no different from NULL,
even though it's serialized in a different way.

Ref: https://github.com/apache/cassandra/blob/7b58b79fe24c612f8bbf7984c325e88496f482d6/doc/native_protocol_v4.spec#L861-L863
  • Loading branch information
psarna authored and jul-stas committed Mar 29, 2022
1 parent 4bf44c7 commit 4cd04cc
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ Value Decoder::decode_value(const DataType::ConstPtr& data_type) {

int32_t count = 0;
if (!data_type->is_collection()) {
if (!is_string_type(data_type->value_type()) && !is_bytes_type(data_type->value_type())
&& size == 0) {
// For legacy reasons, non-string types support "empty" values, e.g. you can have
// an empty int, distinct from NULL. This case used to trigger deserialization errors,
// so now it's simply a fallback to NULL. Another option is to introduce a new "empty"
// state that a value can be in, but it's semantically no different from NULL,
// even though it's serialized in a different way.
// Ref: https://github.com/apache/cassandra/blob/7b58b79fe24c612f8bbf7984c325e88496f482d6/doc/native_protocol_v4.spec#L861-L863
return Value(data_type);
}
return Value(data_type, decoder);
} else if (decoder.decode_int32(count)) {
return Value(data_type, count, decoder);
Expand Down
46 changes: 46 additions & 0 deletions tests/src/unit/tests/test_decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <gtest/gtest.h>

#include "decoder.hpp"
#include "value.hpp"
#include "logger.hpp"

using namespace datastax;
Expand Down Expand Up @@ -1002,3 +1003,48 @@ TEST_F(DecoderUnitTest, DecodeWarnings) {
ASSERT_FALSE(decoder.decode_warnings(value));
ASSERT_TRUE(failure_logged_);
}

TEST_F(DecoderUnitTest, DecodeEmpty) {
CassValueType nonzero_length_scalars[] = {
CASS_VALUE_TYPE_BIGINT,
CASS_VALUE_TYPE_BOOLEAN,
CASS_VALUE_TYPE_COUNTER,
CASS_VALUE_TYPE_DECIMAL,
CASS_VALUE_TYPE_DOUBLE,
CASS_VALUE_TYPE_FLOAT,
CASS_VALUE_TYPE_INT,
CASS_VALUE_TYPE_TIMESTAMP,
CASS_VALUE_TYPE_UUID,
CASS_VALUE_TYPE_TIMEUUID,
CASS_VALUE_TYPE_INET,
CASS_VALUE_TYPE_DATE,
CASS_VALUE_TYPE_TIME,
CASS_VALUE_TYPE_SMALL_INT,
CASS_VALUE_TYPE_TINY_INT,
CASS_VALUE_TYPE_DURATION,
};
CassValueType variable_length_scalars[] = {
CASS_VALUE_TYPE_CUSTOM,
CASS_VALUE_TYPE_ASCII,
CASS_VALUE_TYPE_BLOB,
CASS_VALUE_TYPE_TEXT,
CASS_VALUE_TYPE_VARCHAR,
CASS_VALUE_TYPE_VARINT,
};
for (CassValueType t : nonzero_length_scalars) {
const char input[4] = { 0, 0, 0, 0 };
TestDecoder decoder((const char*)input, 4);
DataType::ConstPtr type_ptr(new DataType(t));
Value val = decoder.decode_value(type_ptr);
// Empty types are deserialized without errors and treated as null
ASSERT_TRUE(val.is_null());
}
for (CassValueType t : variable_length_scalars) {
const char input[4] = { 0, 0, 0, 0 };
TestDecoder decoder((const char*)input, 4);
DataType::ConstPtr type_ptr(new DataType(t));
Value val = decoder.decode_value(type_ptr);
// Empty types with variable size are simply empty, not null
ASSERT_FALSE(val.is_null());
}
}

0 comments on commit 4cd04cc

Please sign in to comment.