Skip to content

Commit

Permalink
Fix tuples with added components (CASSANDRA-15938)
Browse files Browse the repository at this point in the history
  • Loading branch information
blambov committed Jan 17, 2022
1 parent 29d5883 commit b668eea
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
40 changes: 39 additions & 1 deletion src/java/org/apache/cassandra/db/marshal/TupleType.java
Expand Up @@ -203,36 +203,74 @@ private <T> boolean allRemainingComponentsAreNull(T v, ValueAccessor<T> accessor

@Override
public <V> ByteSource asComparableBytes(ValueAccessor<V> accessor, V data, ByteComparable.Version version)
{
switch (version)
{
case LEGACY:
return asComparableBytesLegacy(accessor, data);
case OSS41:
return asComparableBytesNew(accessor, data, version);
default:
throw new AssertionError();
}
}

public <V> ByteSource asComparableBytesLegacy(ValueAccessor<V> accessor, V data)
{
if (accessor.isEmpty(data))
return null;

V[] bufs = split(accessor, data); // this may be shorter than types.size -- other srcs remain null in that case
ByteSource[] srcs = new ByteSource[types.size()];
for (int i = 0; i < bufs.length; ++i)
srcs[i] = bufs[i] != null ? types.get(i).asComparableBytes(accessor, bufs[i], version) : null;
srcs[i] = bufs[i] != null ? types.get(i).asComparableBytes(accessor, bufs[i], ByteComparable.Version.LEGACY) : null;

// We always have a fixed number of sources, with the trailing ones possibly being nulls.
// This can only result in a prefix if the last type in the tuple allows prefixes. Since that type is required
// to be weakly prefix-free, so is the tuple.
return ByteSource.withTerminator(ByteSource.END_OF_STREAM, srcs);
}

public <V> ByteSource asComparableBytesNew(ValueAccessor<V> accessor, V data, ByteComparable.Version version)
{
if (accessor.isEmpty(data))
return null;

V[] bufs = split(accessor, data);
int lengthWithoutTrailingNulls = 0;
for (int i = 0; i < bufs.length; ++i)
if (bufs[i] != null)
lengthWithoutTrailingNulls = i + 1;

ByteSource[] srcs = new ByteSource[lengthWithoutTrailingNulls];
for (int i = 0; i < lengthWithoutTrailingNulls; ++i)
srcs[i] = bufs[i] != null ? types.get(i).asComparableBytes(accessor, bufs[i], version) : null;

// Because we stop early when there are trailing nulls, there needs to be an explicit terminator to make the
// type prefix-free.
return ByteSource.withTerminator(ByteSource.TERMINATOR, srcs);
}

@Override
public <V> V fromComparableBytes(ValueAccessor<V> accessor, ByteSource.Peekable comparableBytes, ByteComparable.Version version)
{
assert version == ByteComparable.Version.OSS41; // Reverse translation is not supported for the legacy version.
if (comparableBytes == null)
return accessor.empty();

V[] componentBuffers = accessor.createArray(types.size());
for (int i = 0; i < types.size(); ++i)
{
if (comparableBytes.peek() == ByteSource.TERMINATOR)
break; // the rest of the fields remain null
AbstractType<?> componentType = types.get(i);
ByteSource.Peekable component = ByteSourceInverse.nextComponentSource(comparableBytes);
if (component != null)
componentBuffers[i] = componentType.fromComparableBytes(accessor, component, version);
else
componentBuffers[i] = null;
}
assert comparableBytes.next() == ByteSource.TERMINATOR;
return buildValue(accessor, componentBuffers);
}

Expand Down
Expand Up @@ -415,6 +415,29 @@ public void testTupleTypeNonFull()
testBuffers(tt, tests);
}

@Test
public void testTupleNewField()
{
TupleType t1 = new TupleType(ImmutableList.of(UTF8Type.instance));
TupleType t2 = new TupleType(ImmutableList.of(UTF8Type.instance, Int32Type.instance));

ByteBuffer vOne = TupleType.buildValue(ByteBufferAccessor.instance, new ByteBuffer[] {decomposeAndRandomPad(UTF8Type.instance, "str")});
ByteBuffer vOneAndNull = TupleType.buildValue(ByteBufferAccessor.instance, new ByteBuffer[] {decomposeAndRandomPad(UTF8Type.instance, "str"),
null});

ByteComparable bOne1 = typeToComparable(t1, vOne);
ByteComparable bOne2 = typeToComparable(t2, vOne);
ByteComparable bOneAndNull2 = typeToComparable(t2, vOneAndNull);

assertEquals("The byte-comparable version of a one-field tuple must be the same as a two-field tuple with non-present second component.",
bOne1.byteComparableAsString(Version.OSS41),
bOne2.byteComparableAsString(Version.OSS41));
assertEquals("The byte-comparable version of a one-field tuple must be the same as a two-field tuple with null as second component.",
bOne1.byteComparableAsString(Version.OSS41),
bOneAndNull2.byteComparableAsString(Version.OSS41));
}


void assertTupleComparesSame(AbstractType t1, AbstractType t2, Object o1, Object o2, Object o3, Object o4)
{
TupleType tt = new TupleType(ImmutableList.of(t1, t2));
Expand Down

0 comments on commit b668eea

Please sign in to comment.