generify collection types patch by slebresnse; reviewed by Paul Cannon for CASSANDRA-4453
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5e5fbc68 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5e5fbc68 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5e5fbc68 Branch: refs/heads/trunk Commit: 5e5fbc6853468e47abe5d25817be1220e90c980f Parents: 1a44fa7 Author: Jonathan Ellis <[email protected]> Authored: Tue Aug 14 16:48:19 2012 -0500 Committer: Jonathan Ellis <[email protected]> Committed: Tue Aug 14 16:48:19 2012 -0500 ---------------------------------------------------------------------- src/java/org/apache/cassandra/cql3/ResultSet.java | 6 +- .../cassandra/cql3/statements/SelectStatement.java | 2 +- .../cassandra/db/marshal/CollectionType.java | 27 +++-- .../org/apache/cassandra/db/marshal/ListType.java | 61 +++++++++-- .../org/apache/cassandra/db/marshal/MapType.java | 81 ++++++++++++--- .../org/apache/cassandra/db/marshal/SetType.java | 66 +++++++++--- 6 files changed, 185 insertions(+), 58 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5fbc68/src/java/org/apache/cassandra/cql3/ResultSet.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/ResultSet.java b/src/java/org/apache/cassandra/cql3/ResultSet.java index 568e3ee..cb5e89f 100644 --- a/src/java/org/apache/cassandra/cql3/ResultSet.java +++ b/src/java/org/apache/cassandra/cql3/ResultSet.java @@ -33,6 +33,7 @@ import org.apache.cassandra.thrift.CqlResult; import org.apache.cassandra.thrift.CqlResultType; import org.apache.cassandra.thrift.CqlRow; import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.utils.FBUtilities; public class ResultSet { @@ -121,7 +122,10 @@ public class ResultSet for (int i = 0; i < metadata.names.size(); i++) { Column col = new Column(ByteBufferUtil.bytes(metadata.names.get(i).toString())); - col.setValue(row.get(i)); + if (row.get(i) != null && metadata.names.get(i).type.isCollection()) + col.setValue(ByteBufferUtil.bytes(FBUtilities.json(metadata.names.get(i).type.compose(row.get(i))))); + else + col.setValue(row.get(i)); thriftCols.add(col); } // The key of CqlRow shoudn't be needed in CQL3 http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5fbc68/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java index 95a1fee..37dd205 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -863,7 +863,7 @@ public class SelectStatement implements CQLStatement if (collection == null) cqlRows.addColumnValue(null); else - cqlRows.addColumnValue(((CollectionType)name.type).serializeForThrift(collection)); + cqlRows.addColumnValue(((CollectionType)name.type).serialize(collection)); break; } IColumn c = columns.getSimple(name.name.key); http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5fbc68/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 21e96d3..a19912b 100644 --- a/src/java/org/apache/cassandra/db/marshal/CollectionType.java +++ b/src/java/org/apache/cassandra/db/marshal/CollectionType.java @@ -30,7 +30,7 @@ import org.apache.cassandra.utils.Pair; * Please note that this comparator shouldn't be used "manually" (through thrift for instance). * */ -public abstract class CollectionType extends AbstractType<ByteBuffer> +public abstract class CollectionType<T> extends AbstractType<T> { public enum Kind { @@ -49,7 +49,7 @@ public abstract class CollectionType extends AbstractType<ByteBuffer> protected abstract void appendToStringBuilder(StringBuilder sb); - public abstract ByteBuffer serializeForThrift(List<Pair<ByteBuffer, IColumn>> columns); + public abstract ByteBuffer serialize(List<Pair<ByteBuffer, IColumn>> columns); @Override public String toString() @@ -64,16 +64,6 @@ public abstract class CollectionType extends AbstractType<ByteBuffer> throw new UnsupportedOperationException("CollectionType should not be use directly as a comparator"); } - public ByteBuffer compose(ByteBuffer bytes) - { - return BytesType.instance.compose(bytes); - } - - public ByteBuffer decompose(ByteBuffer value) - { - return BytesType.instance.decompose(value); - } - public String getString(ByteBuffer bytes) { return BytesType.instance.getString(bytes); @@ -100,4 +90,17 @@ public abstract class CollectionType extends AbstractType<ByteBuffer> { return true; } + + // Utilitary method + protected ByteBuffer pack(List<ByteBuffer> buffers, int elements, int size) + { + ByteBuffer result = ByteBuffer.allocate(2 + size); + result.putShort((short)elements); + for (ByteBuffer bb : buffers) + { + result.putShort((short)bb.remaining()); + result.put(bb.duplicate()); + } + return (ByteBuffer)result.flip(); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5fbc68/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 ad1211d..f18fc00 100644 --- a/src/java/org/apache/cassandra/db/marshal/ListType.java +++ b/src/java/org/apache/cassandra/db/marshal/ListType.java @@ -26,14 +26,14 @@ import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.Pair; -public class ListType extends CollectionType +public class ListType<T> extends CollectionType<List<T>> { // interning instances private static final Map<AbstractType<?>, ListType> instances = new HashMap<AbstractType<?>, ListType>(); - public final AbstractType<?> elements; + public final AbstractType<T> elements; - public static ListType getInstance(TypeParser parser) throws ConfigurationException + public static ListType<?> getInstance(TypeParser parser) throws ConfigurationException { List<AbstractType<?>> l = parser.getTypeParameters(); if (l.size() != 1) @@ -42,7 +42,7 @@ public class ListType extends CollectionType return getInstance(l.get(0)); } - public static synchronized ListType getInstance(AbstractType<?> elements) + public static synchronized <T> ListType<T> getInstance(AbstractType<T> elements) { ListType t = instances.get(elements); if (t == null) @@ -53,32 +53,71 @@ public class ListType extends CollectionType return t; } - private ListType(AbstractType<?> elements) + private ListType(AbstractType<T> elements) { super(Kind.LIST); this.elements = elements; } - public AbstractType<?> nameComparator() + public AbstractType<UUID> nameComparator() { return TimeUUIDType.instance; } - public AbstractType<?> valueComparator() + public AbstractType<T> valueComparator() { return elements; } + public List<T> compose(ByteBuffer bytes) + { + ByteBuffer input = bytes.duplicate(); + int n = input.getShort(); + List<T> l = new ArrayList<T>(n); + for (int i = 0; i < n; i++) + { + int s = input.getShort(); + byte[] data = new byte[s]; + input.get(data); + l.add(elements.compose(ByteBuffer.wrap(data))); + } + return l; + } + + /** + * Layout is: {@code <n><s_1><b_1>...<s_n><b_n> } + * where: + * n is the number of elements + * s_i is the number of bytes composing the ith element + * b_i is the s_i bytes composing the ith element + */ + public ByteBuffer decompose(List<T> value) + { + List<ByteBuffer> bbs = new ArrayList<ByteBuffer>(value.size()); + int size = 0; + for (T elt : value) + { + ByteBuffer bb = elements.decompose(elt); + bbs.add(bb); + size += 2 + bb.remaining(); + } + return pack(bbs, value.size(), size); + } + protected void appendToStringBuilder(StringBuilder sb) { sb.append(getClass().getName()).append(TypeParser.stringifyTypeParameters(Collections.<AbstractType<?>>singletonList(elements))); } - public ByteBuffer serializeForThrift(List<Pair<ByteBuffer, IColumn>> columns) + public ByteBuffer serialize(List<Pair<ByteBuffer, IColumn>> columns) { - List<Object> l = new ArrayList<Object>(columns.size()); + List<ByteBuffer> bbs = new ArrayList<ByteBuffer>(columns.size()); + int size = 0; for (Pair<ByteBuffer, IColumn> p : columns) - l.add(elements.compose(p.right.value())); - return ByteBufferUtil.bytes(FBUtilities.json(l)); + { + bbs.add(p.right.value()); + size += 2 + p.right.value().remaining(); + } + return pack(bbs, columns.size(), size); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5fbc68/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 a5a6caa..2ac65cf 100644 --- a/src/java/org/apache/cassandra/db/marshal/MapType.java +++ b/src/java/org/apache/cassandra/db/marshal/MapType.java @@ -18,11 +18,7 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; import org.apache.cassandra.db.IColumn; import org.apache.cassandra.config.ConfigurationException; @@ -30,15 +26,15 @@ import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.Pair; -public class MapType extends CollectionType +public class MapType<K, V> extends CollectionType<Map<K, V>> { // interning instances private static final Map<Pair<AbstractType<?>, AbstractType<?>>, MapType> instances = new HashMap<Pair<AbstractType<?>, AbstractType<?>>, MapType>(); - public final AbstractType<?> keys; - public final AbstractType<?> values; + public final AbstractType<K> keys; + public final AbstractType<V> values; - public static MapType getInstance(TypeParser parser) throws ConfigurationException + public static MapType<?, ?> getInstance(TypeParser parser) throws ConfigurationException { List<AbstractType<?>> l = parser.getTypeParameters(); if (l.size() != 2) @@ -47,7 +43,7 @@ public class MapType extends CollectionType return getInstance(l.get(0), l.get(1)); } - public static synchronized MapType getInstance(AbstractType<?> keys, AbstractType<?> values) + public static synchronized <K, V> MapType<K, V> getInstance(AbstractType<K> keys, AbstractType<V> values) { Pair<AbstractType<?>, AbstractType<?>> p = Pair.<AbstractType<?>, AbstractType<?>>create(keys, values); MapType t = instances.get(p); @@ -59,33 +55,84 @@ public class MapType extends CollectionType return t; } - private MapType(AbstractType<?> keys, AbstractType<?> values) + private MapType(AbstractType<K> keys, AbstractType<V> values) { super(Kind.MAP); this.keys = keys; this.values = values; } - public AbstractType<?> nameComparator() + public AbstractType<K> nameComparator() { return keys; } - public AbstractType<?> valueComparator() + public AbstractType<V> valueComparator() { return values; } + public Map<K, V> compose(ByteBuffer bytes) + { + ByteBuffer input = bytes.duplicate(); + int n = input.getShort(); + Map<K, V> m = new LinkedHashMap<K, V>(n); + for (int i = 0; i < n; i++) + { + int sk = input.getShort(); + byte[] datak = new byte[sk]; + input.get(datak); + + int sv = input.getShort(); + byte[] datav = new byte[sv]; + input.get(datav); + m.put(keys.compose(ByteBuffer.wrap(datak)), values.compose(ByteBuffer.wrap(datav))); + } + return m; + } + + /** + * Layout is: {@code <n><sk_1><k_1><sv_1><v_1>...<sk_n><k_n><sv_n><v_n> } + * where: + * n is the number of elements + * sk_i is the number of bytes composing the ith key k_i + * k_i is the sk_i bytes composing the ith key + * sv_i is the number of bytes composing the ith value v_i + * v_i is the sv_i bytes composing the ith value + */ + public ByteBuffer decompose(Map<K, V> value) + { + List<ByteBuffer> bbs = new ArrayList<ByteBuffer>(2 * value.size()); + int size = 0; + for (Map.Entry<K, V> entry : value.entrySet()) + { + ByteBuffer bbk = keys.decompose(entry.getKey()); + ByteBuffer bbv = values.decompose(entry.getValue()); + bbs.add(bbk); + bbs.add(bbv); + size += 4 + bbk.remaining() + bbv.remaining(); + } + return pack(bbs, value.size(), size); + } + protected void appendToStringBuilder(StringBuilder sb) { sb.append(getClass().getName()).append(TypeParser.stringifyTypeParameters(Arrays.asList(keys, values))); } - public ByteBuffer serializeForThrift(List<Pair<ByteBuffer, IColumn>> columns) + /** + * Creates the same output than decompose, but from the internal representation. + */ + public ByteBuffer serialize(List<Pair<ByteBuffer, IColumn>> columns) { - Map<String, Object> m = new LinkedHashMap<String, Object>(); + List<ByteBuffer> bbs = new ArrayList<ByteBuffer>(2 * columns.size()); + int size = 0; for (Pair<ByteBuffer, IColumn> p : columns) - m.put(keys.getString(p.left), values.compose(p.right.value())); - return ByteBufferUtil.bytes(FBUtilities.json(m)); + { + bbs.add(p.left); + bbs.add(p.right.value()); + size += 4 + p.left.remaining() + p.right.value().remaining(); + } + return pack(bbs, columns.size(), size); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5fbc68/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 a4fa9e3..7c71baf 100644 --- a/src/java/org/apache/cassandra/db/marshal/SetType.java +++ b/src/java/org/apache/cassandra/db/marshal/SetType.java @@ -18,11 +18,7 @@ package org.apache.cassandra.db.marshal; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.apache.cassandra.db.IColumn; import org.apache.cassandra.config.ConfigurationException; @@ -30,14 +26,14 @@ import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.Pair; -public class SetType extends CollectionType +public class SetType<T> extends CollectionType<Set<T>> { // interning instances private static final Map<AbstractType<?>, SetType> instances = new HashMap<AbstractType<?>, SetType>(); - public final AbstractType<?> elements; + public final AbstractType<T> elements; - public static SetType getInstance(TypeParser parser) throws ConfigurationException + public static SetType<?> getInstance(TypeParser parser) throws ConfigurationException { List<AbstractType<?>> l = parser.getTypeParameters(); if (l.size() != 1) @@ -46,7 +42,7 @@ public class SetType extends CollectionType return getInstance(l.get(0)); } - public static synchronized SetType getInstance(AbstractType<?> elements) + public static synchronized <T> SetType getInstance(AbstractType<T> elements) { SetType t = instances.get(elements); if (t == null) @@ -57,13 +53,13 @@ public class SetType extends CollectionType return t; } - public SetType(AbstractType<?> elements) + public SetType(AbstractType<T> elements) { super(Kind.SET); this.elements = elements; } - public AbstractType<?> nameComparator() + public AbstractType<T> nameComparator() { return elements; } @@ -73,17 +69,55 @@ public class SetType extends CollectionType return EmptyType.instance; } + public Set<T> compose(ByteBuffer bytes) + { + ByteBuffer input = bytes.duplicate(); + int n = input.getShort(); + Set<T> l = new LinkedHashSet<T>(n); + for (int i = 0; i < n; i++) + { + int s = input.getShort(); + byte[] data = new byte[s]; + input.get(data); + l.add(elements.compose(ByteBuffer.wrap(data))); + } + return l; + } + + /** + * Layout is: {@code <n><s_1><b_1>...<s_n><b_n> } + * where: + * n is the number of elements + * s_i is the number of bytes composing the ith element + * b_i is the s_i bytes composing the ith element + */ + public ByteBuffer decompose(Set<T> value) + { + List<ByteBuffer> bbs = new ArrayList<ByteBuffer>(value.size()); + int size = 0; + for (T elt : value) + { + ByteBuffer bb = elements.decompose(elt); + bbs.add(bb); + size += 2 + bb.remaining(); + } + return pack(bbs, value.size(), size); + } + protected void appendToStringBuilder(StringBuilder sb) { sb.append(getClass().getName()).append(TypeParser.stringifyTypeParameters(Collections.<AbstractType<?>>singletonList(elements))); } - public ByteBuffer serializeForThrift(List<Pair<ByteBuffer, IColumn>> columns) + public ByteBuffer serialize(List<Pair<ByteBuffer, IColumn>> columns) { - // We're using a list for now, since json doesn't have maps - List<Object> l = new ArrayList<Object>(columns.size()); + List<ByteBuffer> bbs = new ArrayList<ByteBuffer>(columns.size()); + int size = 0; for (Pair<ByteBuffer, IColumn> p : columns) - l.add(elements.compose(p.left)); - return ByteBufferUtil.bytes(FBUtilities.json(l)); + { + bbs.add(p.left); + size += 2 + p.left.remaining(); + } + return pack(bbs, columns.size(), size); } }
