mirror of https://github.com/apache/cassandra
Make sub-range selection for non-frozen collections return null instead of empty
patch by Benjamin Lerer; reviewed by Andrés de la Peña for CASSANDRA-14182
This commit is contained in:
parent
9635c6041a
commit
4de7a65ed9
|
|
@ -1,4 +1,5 @@
|
|||
4.0
|
||||
* Make sub-range selection for non-frozen collections return null instead of empty (CASSANDRA-14182)
|
||||
* BloomFilter serialization format should not change byte ordering (CASSANDRA-9067)
|
||||
* Remove unused on-heap BloomFilter implementation (CASSANDRA-14152)
|
||||
* Delete temp test files on exit (CASSANDRA-14153)
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ abstract class ElementsSelector extends Selector
|
|||
|
||||
protected ByteBuffer extractSelection(ByteBuffer collection)
|
||||
{
|
||||
return type.getSerializer().getSliceFromSerialized(collection, from, to, type.nameComparator());
|
||||
return type.getSerializer().getSliceFromSerialized(collection, from, to, type.nameComparator(), type.isFrozenCollection());
|
||||
}
|
||||
|
||||
public AbstractType<?> getType()
|
||||
|
|
|
|||
|
|
@ -134,6 +134,8 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T>
|
|||
|
||||
/**
|
||||
* Returns the slice of a collection directly from its serialized value.
|
||||
* <p>If the slice contains no elements an empty collection will be returned for frozen collections, and a
|
||||
* {@code null} one for non-frozen collections.</p>
|
||||
*
|
||||
* @param collection the serialized collection. This cannot be {@code null}.
|
||||
* @param from the left bound of the slice to extract. This cannot be {@code null} but if this is
|
||||
|
|
@ -141,10 +143,14 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T>
|
|||
* of {@code collection}.
|
||||
* @param comparator the type to use to compare the {@code from} and {@code to} values to those
|
||||
* in the collection.
|
||||
* @return a valid serialized collection (possibly empty) corresponding to slice {@code [from, to]}
|
||||
* of {@code collection}.
|
||||
* @param frozen {@code true} if the collection is a frozen one, {@code false} otherwise
|
||||
* @return a serialized collection corresponding to slice {@code [from, to]} of {@code collection}.
|
||||
*/
|
||||
public abstract ByteBuffer getSliceFromSerialized(ByteBuffer collection, ByteBuffer from, ByteBuffer to, AbstractType<?> comparator);
|
||||
public abstract ByteBuffer getSliceFromSerialized(ByteBuffer collection,
|
||||
ByteBuffer from,
|
||||
ByteBuffer to,
|
||||
AbstractType<?> comparator,
|
||||
boolean frozen);
|
||||
|
||||
/**
|
||||
* Creates a new serialized map composed from the data from {@code input} between {@code startPos}
|
||||
|
|
|
|||
|
|
@ -169,13 +169,19 @@ public class ListSerializer<T> extends CollectionSerializer<List<T>>
|
|||
return (Class) List.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSerializedValue(ByteBuffer collection, ByteBuffer key, AbstractType<?> comparator)
|
||||
{
|
||||
// We don't allow selecting an element of a list so we don't need this.
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public ByteBuffer getSliceFromSerialized(ByteBuffer collection, ByteBuffer from, ByteBuffer to, AbstractType<?> comparator)
|
||||
@Override
|
||||
public ByteBuffer getSliceFromSerialized(ByteBuffer collection,
|
||||
ByteBuffer from,
|
||||
ByteBuffer to,
|
||||
AbstractType<?> comparator,
|
||||
boolean frozen)
|
||||
{
|
||||
// We don't allow slicing of list so we don't need this.
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ public class MapSerializer<K, V> extends CollectionSerializer<Map<K, V>>
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSerializedValue(ByteBuffer collection, ByteBuffer key, AbstractType<?> comparator)
|
||||
{
|
||||
try
|
||||
|
|
@ -155,7 +156,12 @@ public class MapSerializer<K, V> extends CollectionSerializer<Map<K, V>>
|
|||
}
|
||||
}
|
||||
|
||||
public ByteBuffer getSliceFromSerialized(ByteBuffer collection, ByteBuffer from, ByteBuffer to, AbstractType<?> comparator)
|
||||
@Override
|
||||
public ByteBuffer getSliceFromSerialized(ByteBuffer collection,
|
||||
ByteBuffer from,
|
||||
ByteBuffer to,
|
||||
AbstractType<?> comparator,
|
||||
boolean frozen)
|
||||
{
|
||||
if (from == ByteBufferUtil.UNSET_BYTE_BUFFER && to == ByteBufferUtil.UNSET_BYTE_BUFFER)
|
||||
return collection;
|
||||
|
|
@ -208,6 +214,10 @@ public class MapSerializer<K, V> extends CollectionSerializer<Map<K, V>>
|
|||
if (comparison == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (count == 0 && !frozen)
|
||||
return null;
|
||||
|
||||
return copyAsNewCollection(collection, count, startPos, input.position(), ProtocolVersion.V3);
|
||||
}
|
||||
catch (BufferUnderflowException e)
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ public class SetSerializer<T> extends CollectionSerializer<Set<T>>
|
|||
return (Class) Set.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSerializedValue(ByteBuffer collection, ByteBuffer key, AbstractType<?> comparator)
|
||||
{
|
||||
try
|
||||
|
|
@ -165,7 +166,12 @@ public class SetSerializer<T> extends CollectionSerializer<Set<T>>
|
|||
}
|
||||
}
|
||||
|
||||
public ByteBuffer getSliceFromSerialized(ByteBuffer collection, ByteBuffer from, ByteBuffer to, AbstractType<?> comparator)
|
||||
@Override
|
||||
public ByteBuffer getSliceFromSerialized(ByteBuffer collection,
|
||||
ByteBuffer from,
|
||||
ByteBuffer to,
|
||||
AbstractType<?> comparator,
|
||||
boolean frozen)
|
||||
{
|
||||
if (from == ByteBufferUtil.UNSET_BYTE_BUFFER && to == ByteBufferUtil.UNSET_BYTE_BUFFER)
|
||||
return collection;
|
||||
|
|
@ -216,6 +222,10 @@ public class SetSerializer<T> extends CollectionSerializer<Set<T>>
|
|||
if (comparison == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (count == 0 && !frozen)
|
||||
return null;
|
||||
|
||||
return copyAsNewCollection(collection, count, startPos, input.position(), ProtocolVersion.V3);
|
||||
}
|
||||
catch (BufferUnderflowException e)
|
||||
|
|
|
|||
|
|
@ -1198,7 +1198,7 @@ public class CollectionsTest extends CQLTester
|
|||
|
||||
assertRows(execute("SELECT k, l, m['2'..'3'], o FROM %s WHERE k = 0"),
|
||||
row(0, "foobar", map("22", "value22"), 42),
|
||||
row(0, "foobar", map(), 42)
|
||||
row(0, "foobar", null, 42)
|
||||
);
|
||||
|
||||
assertRows(execute("SELECT k, l, m['22'..], o FROM %s WHERE k = 0"),
|
||||
|
|
@ -1711,7 +1711,7 @@ public class CollectionsTest extends CQLTester
|
|||
|
||||
assertRows(result,
|
||||
row(0,
|
||||
map("1", "one", "2", "two"), "two", map("2", "two"), map("1", "one", "2", "two"), map(),
|
||||
map("1", "one", "2", "two"), "two", map("2", "two"), map("1", "one", "2", "two"), null,
|
||||
map("1", "one", "2", "two"), "two", map("2", "two"), map("1", "one", "2", "two"), map(),
|
||||
set("1", "2", "3"), "2", set("2", "3"), set("1", "2"), set("3"),
|
||||
set("1", "2", "3"), "2", set("2", "3"), set("1", "2"), set("3")));
|
||||
|
|
@ -1815,7 +1815,7 @@ public class CollectionsTest extends CQLTester
|
|||
flush();
|
||||
|
||||
assertRows(execute("SELECT m[7..8] FROM %s WHERE k=?", 0),
|
||||
row(map()));
|
||||
row((Map<Integer, Integer>) null));
|
||||
|
||||
assertRows(execute("SELECT m[0..3] FROM %s WHERE k=?", 0),
|
||||
row(map(0, 0, 1, 1, 2, 2, 3, 3)));
|
||||
|
|
@ -1915,4 +1915,68 @@ public class CollectionsTest extends CQLTester
|
|||
assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
|
||||
"INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectionOfEmptyCollections() throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<text, int>>, s frozen<set<int>>)");
|
||||
|
||||
execute("INSERT INTO %s(k) VALUES (0)");
|
||||
execute("INSERT INTO %s(k, m, s) VALUES (1, {}, {})");
|
||||
execute("INSERT INTO %s(k, m, s) VALUES (2, ?, ?)", map(), set());
|
||||
execute("INSERT INTO %s(k, m, s) VALUES (3, {'2':2}, {2})");
|
||||
|
||||
beforeAndAfterFlush(() ->
|
||||
{
|
||||
assertRows(execute("SELECT m, s FROM %s WHERE k = 0"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 0"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 0"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 0"), row(null, null));
|
||||
|
||||
assertRows(execute("SELECT m, s FROM %s WHERE k = 1"), row(map(), set()));
|
||||
assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 1"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 1"), row(map(), set()));
|
||||
assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 1"), row(map(), set()));
|
||||
|
||||
assertRows(execute("SELECT m, s FROM %s WHERE k = 2"), row(map(), set()));
|
||||
assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 2"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 2"), row(map(), set()));
|
||||
assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 2"), row(map(), set()));
|
||||
|
||||
assertRows(execute("SELECT m, s FROM %s WHERE k = 3"), row(map("2", 2), set(2)));
|
||||
assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 3"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 3"), row(map(), set()));
|
||||
assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 3"), row(map(), set()));
|
||||
});
|
||||
|
||||
createTable("CREATE TABLE %s (k int PRIMARY KEY, m map<text, int>, s set<int>)");
|
||||
|
||||
execute("INSERT INTO %s(k) VALUES (0)");
|
||||
execute("INSERT INTO %s(k, m, s) VALUES (1, {}, {})");
|
||||
execute("INSERT INTO %s(k, m, s) VALUES (2, ?, ?)", map(), set());
|
||||
execute("INSERT INTO %s(k, m, s) VALUES (3, {'2':2}, {2})");
|
||||
|
||||
beforeAndAfterFlush(() ->
|
||||
{
|
||||
assertRows(execute("SELECT m, s FROM %s WHERE k = 0"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 0"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 0"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 0"), row(null, null));
|
||||
|
||||
assertRows(execute("SELECT m, s FROM %s WHERE k = 1"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 1"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 1"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 1"), row(null, null));
|
||||
|
||||
assertRows(execute("SELECT m, s FROM %s WHERE k = 2"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 2"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 2"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 2"), row(null, null));
|
||||
|
||||
assertRows(execute("SELECT m, s FROM %s WHERE k = 3"), row(map("2", 2), set(2)));
|
||||
assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 3"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 3"), row(null, null));
|
||||
assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 3"), row(null, null));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue