Repository: cassandra Updated Branches: refs/heads/trunk 335aa9f1f -> 233cc8b29
Add proper compare function for CollectionType (for UDT sake) patch by slebresne; reviewed by thobbs for CASSANDRA-6783 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/f4b9f161 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/f4b9f161 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/f4b9f161 Branch: refs/heads/trunk Commit: f4b9f16114947e708331899bdb15f8daf5100a3a Parents: e23e57f Author: Sylvain Lebresne <[email protected]> Authored: Thu Mar 20 10:10:45 2014 +0100 Committer: Sylvain Lebresne <[email protected]> Committed: Thu Mar 20 10:10:45 2014 +0100 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../cassandra/db/marshal/CollectionType.java | 5 - .../apache/cassandra/db/marshal/ListType.java | 33 ++++++ .../apache/cassandra/db/marshal/MapType.java | 34 ++++++ .../apache/cassandra/db/marshal/SetType.java | 6 + .../db/marshal/CollectionTypeTest.java | 116 +++++++++++++++++++ 6 files changed, 190 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4b9f161/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 159d242..856ec23 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -23,6 +23,7 @@ * Add logging levels (minimal, normal or verbose) to stress tool (CASSANDRA-6849) * Fix race condition in Batch CLE (CASSANDRA-6860) * Improve cleanup/scrub/upgradesstables failure handling (CASSANDRA-6774) + * Proper compare function for CollectionType (CASSANDRA-6783) Merged from 2.0: * Omit tombstones from schema digests (CASSANDRA-6862) * Include correct consistencyLevel in LWT timeout (CASSANDRA-6884) http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4b9f161/src/java/org/apache/cassandra/db/marshal/CollectionType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/CollectionType.java b/src/java/org/apache/cassandra/db/marshal/CollectionType.java index 02d01ff..5db4ba0 100644 --- a/src/java/org/apache/cassandra/db/marshal/CollectionType.java +++ b/src/java/org/apache/cassandra/db/marshal/CollectionType.java @@ -67,11 +67,6 @@ public abstract class CollectionType<T> extends AbstractType<T> return sb.toString(); } - public int compare(ByteBuffer o1, ByteBuffer o2) - { - throw new UnsupportedOperationException("CollectionType should not be use directly as a comparator"); - } - public String getString(ByteBuffer bytes) { return BytesType.instance.getString(bytes); http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4b9f161/src/java/org/apache/cassandra/db/marshal/ListType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/ListType.java b/src/java/org/apache/cassandra/db/marshal/ListType.java index eabda0b..1cc0425 100644 --- a/src/java/org/apache/cassandra/db/marshal/ListType.java +++ b/src/java/org/apache/cassandra/db/marshal/ListType.java @@ -25,6 +25,7 @@ import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.ListSerializer; +import org.apache.cassandra.utils.ByteBufferUtil; public class ListType<T> extends CollectionType<List<T>> { @@ -76,6 +77,38 @@ public class ListType<T> extends CollectionType<List<T>> return serializer; } + @Override + public int compare(ByteBuffer o1, ByteBuffer o2) + { + return compareListOrSet(elements, o1, o2); + } + + static int compareListOrSet(AbstractType<?> elementsComparator, ByteBuffer o1, ByteBuffer o2) + { + // Note that this is only used if the collection is inside an UDT + if (o1 == null || !o1.hasRemaining()) + return o2 == null || !o2.hasRemaining() ? 0 : -1; + if (o2 == null || !o2.hasRemaining()) + return 1; + + ByteBuffer bb1 = o1.duplicate(); + ByteBuffer bb2 = o2.duplicate(); + + int size1 = ByteBufferUtil.readShortLength(bb1); + int size2 = ByteBufferUtil.readShortLength(bb2); + + for (int i = 0; i < Math.min(size1, size2); i++) + { + ByteBuffer v1 = ByteBufferUtil.readBytesWithShortLength(bb1); + ByteBuffer v2 = ByteBufferUtil.readBytesWithShortLength(bb2); + int cmp = elementsComparator.compare(v1, v2); + if (cmp != 0) + return cmp; + } + + return size1 == size2 ? 0 : (size1 < size2 ? -1 : 1); + } + protected void appendToStringBuilder(StringBuilder sb) { sb.append(getClass().getName()).append(TypeParser.stringifyTypeParameters(Collections.<AbstractType<?>>singletonList(elements))); http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4b9f161/src/java/org/apache/cassandra/db/marshal/MapType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/MapType.java b/src/java/org/apache/cassandra/db/marshal/MapType.java index 4d834e7..2e693d6 100644 --- a/src/java/org/apache/cassandra/db/marshal/MapType.java +++ b/src/java/org/apache/cassandra/db/marshal/MapType.java @@ -26,6 +26,7 @@ import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.TypeSerializer; import org.apache.cassandra.serializers.MapSerializer; import org.apache.cassandra.utils.Pair; +import org.apache.cassandra.utils.ByteBufferUtil; public class MapType<K, V> extends CollectionType<Map<K, V>> { @@ -76,6 +77,39 @@ public class MapType<K, V> extends CollectionType<Map<K, V>> } @Override + public int compare(ByteBuffer o1, ByteBuffer o2) + { + // Note that this is only used if the collection is inside an UDT + if (o1 == null || !o1.hasRemaining()) + return o2 == null || !o2.hasRemaining() ? 0 : -1; + if (o2 == null || !o2.hasRemaining()) + return 1; + + ByteBuffer bb1 = o1.duplicate(); + ByteBuffer bb2 = o2.duplicate(); + + int size1 = ByteBufferUtil.readShortLength(bb1); + int size2 = ByteBufferUtil.readShortLength(bb2); + + for (int i = 0; i < Math.min(size1, size2); i++) + { + ByteBuffer k1 = ByteBufferUtil.readBytesWithShortLength(bb1); + ByteBuffer k2 = ByteBufferUtil.readBytesWithShortLength(bb2); + int cmp = keys.compare(k1, k2); + if (cmp != 0) + return cmp; + + ByteBuffer v1 = ByteBufferUtil.readBytesWithShortLength(bb1); + ByteBuffer v2 = ByteBufferUtil.readBytesWithShortLength(bb2); + cmp = values.compare(v1, v2); + if (cmp != 0) + return cmp; + } + + return size1 == size2 ? 0 : (size1 < size2 ? -1 : 1); + } + + @Override public TypeSerializer<Map<K, V>> getSerializer() { return serializer; http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4b9f161/src/java/org/apache/cassandra/db/marshal/SetType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/SetType.java b/src/java/org/apache/cassandra/db/marshal/SetType.java index 292b832..5c13c2e 100644 --- a/src/java/org/apache/cassandra/db/marshal/SetType.java +++ b/src/java/org/apache/cassandra/db/marshal/SetType.java @@ -71,6 +71,12 @@ public class SetType<T> extends CollectionType<Set<T>> return EmptyType.instance; } + @Override + public int compare(ByteBuffer o1, ByteBuffer o2) + { + return ListType.compareListOrSet(elements, o1, o2); + } + public TypeSerializer<Set<T>> getSerializer() { return serializer; http://git-wip-us.apache.org/repos/asf/cassandra/blob/f4b9f161/test/unit/org/apache/cassandra/db/marshal/CollectionTypeTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/db/marshal/CollectionTypeTest.java b/test/unit/org/apache/cassandra/db/marshal/CollectionTypeTest.java new file mode 100644 index 0000000..fba4742 --- /dev/null +++ b/test/unit/org/apache/cassandra/db/marshal/CollectionTypeTest.java @@ -0,0 +1,116 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.db.marshal; + +import java.nio.ByteBuffer; +import java.util.*; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +import org.apache.cassandra.utils.ByteBufferUtil; + +public class CollectionTypeTest +{ + @Test + public void testListComparison() + { + ListType<String> lt = ListType.getInstance(UTF8Type.instance); + + ByteBuffer[] lists = new ByteBuffer[] { + ByteBufferUtil.EMPTY_BYTE_BUFFER, + lt.decompose(ImmutableList.<String>of()), + lt.decompose(ImmutableList.of("aa")), + lt.decompose(ImmutableList.of("bb")), + lt.decompose(ImmutableList.of("bb", "cc")), + lt.decompose(ImmutableList.of("bb", "dd")) + }; + + for (int i = 0; i < lists.length; i++) + assertEquals(lt.compare(lists[i], lists[i]), 0); + + for (int i = 0; i < lists.length-1; i++) + { + for (int j = i+1; j < lists.length; j++) + { + assertEquals(lt.compare(lists[i], lists[j]), -1); + assertEquals(lt.compare(lists[j], lists[i]), 1); + } + } + } + + @Test + public void testSetComparison() + { + SetType<String> st = SetType.getInstance(UTF8Type.instance); + + ByteBuffer[] sets = new ByteBuffer[] { + ByteBufferUtil.EMPTY_BYTE_BUFFER, + st.decompose(ImmutableSet.<String>of()), + st.decompose(ImmutableSet.of("aa")), + st.decompose(ImmutableSet.of("bb")), + st.decompose(ImmutableSet.of("bb", "cc")), + st.decompose(ImmutableSet.of("bb", "dd")) + }; + + for (int i = 0; i < sets.length; i++) + assertEquals(st.compare(sets[i], sets[i]), 0); + + for (int i = 0; i < sets.length-1; i++) + { + for (int j = i+1; j < sets.length; j++) + { + assertEquals(st.compare(sets[i], sets[j]), -1); + assertEquals(st.compare(sets[j], sets[i]), 1); + } + } + } + + @Test + public void testMapComparison() + { + MapType<String, String> mt = MapType.getInstance(UTF8Type.instance, UTF8Type.instance); + + ByteBuffer[] maps = new ByteBuffer[] { + ByteBufferUtil.EMPTY_BYTE_BUFFER, + mt.decompose(ImmutableMap.<String, String>of()), + mt.decompose(ImmutableMap.of("aa", "val1")), + mt.decompose(ImmutableMap.of("aa", "val2")), + mt.decompose(ImmutableMap.of("bb", "val1")), + mt.decompose(ImmutableMap.of("bb", "val1", "cc", "val3")), + mt.decompose(ImmutableMap.of("bb", "val1", "dd", "val3")), + mt.decompose(ImmutableMap.of("bb", "val1", "dd", "val4")) + }; + + for (int i = 0; i < maps.length; i++) + assertEquals(mt.compare(maps[i], maps[i]), 0); + + for (int i = 0; i < maps.length-1; i++) + { + for (int j = i+1; j < maps.length; j++) + { + assertEquals(mt.compare(maps[i], maps[j]), -1); + assertEquals(mt.compare(maps[j], maps[i]), 1); + } + } + } +}
