Author: slebresne
Date: Mon May 9 17:24:34 2011
New Revision: 1101120
URL: http://svn.apache.org/viewvc?rev=1101120&view=rev
Log:
Add CompositeType and DynamicCompositeType
patch by slebresne; reviewed by edanuff for CASSANDRA-2231
Added:
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java
Modified:
cassandra/branches/cassandra-0.8.1/CHANGES.txt
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java
Modified: cassandra/branches/cassandra-0.8.1/CHANGES.txt
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/CHANGES.txt?rev=1101120&r1=1101119&r2=1101120&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8.1/CHANGES.txt (original)
+++ cassandra/branches/cassandra-0.8.1/CHANGES.txt Mon May 9 17:24:34 2011
@@ -6,6 +6,7 @@
0.8.1
* add support for comparator parameters and a generic ReverseType
(CASSANDRA-2355)
+ * add CompositeType and DynamicCompositeType (CASSANDRA-2231)
0.8.0-?
* faster flushes and compaction from fixing excessively pessimistic
Added:
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java?rev=1101120&view=auto
==============================================================================
---
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java
(added)
+++
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java
Mon May 9 17:24:34 2011
@@ -0,0 +1,294 @@
+/**
+ * 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.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.sql.Types;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+/**
+ * A class avoiding class duplication between CompositeType and
+ * DynamicCompositeType.
+ * Those two differs only in that for DynamicCompositeType, the comparators
+ * are in the encoded column name at the front of each component.
+ */
+public abstract class AbstractCompositeType extends AbstractType<ByteBuffer>
+{
+ // changes bb position
+ protected static int getShortLength(ByteBuffer bb)
+ {
+ int length = (bb.get() & 0xFF) << 8;
+ return length | (bb.get() & 0xFF);
+ }
+
+ // changes bb position
+ protected static void putShortLength(ByteBuffer bb, int length)
+ {
+ bb.put((byte) ((length >> 8) & 0xFF));
+ bb.put((byte) (length & 0xFF));
+ }
+
+ // changes bb position
+ protected static ByteBuffer getBytes(ByteBuffer bb, int length)
+ {
+ ByteBuffer copy = bb.duplicate();
+ copy.limit(copy.position() + length);
+ bb.position(bb.position() + length);
+ return copy;
+ }
+
+ // changes bb position
+ protected static ByteBuffer getWithShortLength(ByteBuffer bb)
+ {
+ int length = getShortLength(bb);
+ return getBytes(bb, length);
+ }
+
+ public int compare(ByteBuffer o1, ByteBuffer o2)
+ {
+ if (null == o1)
+ return null == o2 ? 0 : -1;
+
+ ByteBuffer bb1 = o1.duplicate();
+ ByteBuffer bb2 = o2.duplicate();
+ int i = 0;
+
+ while (bb1.remaining() > 0 && bb2.remaining() > 0)
+ {
+ AbstractType comparator = getNextComparator(i, bb1, bb2);
+
+ ByteBuffer value1 = getWithShortLength(bb1);
+ ByteBuffer value2 = getWithShortLength(bb2);
+
+ int cmp = comparator.compare(value1, value2);
+ if (cmp != 0)
+ return cmp;
+
+ byte b1 = bb1.get();
+ byte b2 = bb2.get();
+ if (b1 < 0)
+ {
+ if (b2 >= 0)
+ return -1;
+ }
+ else if (b1 > 0)
+ {
+ if (b2 <= 0)
+ return 1;
+ }
+ else
+ {
+ // b1 == 0
+ if (b2 != 0)
+ return -b2;
+ }
+ ++i;
+ }
+
+ if (bb1.remaining() == 0)
+ return bb2.remaining() == 0 ? 0 : -1;
+
+ // bb1.remaining() > 0 && bb2.remaining() == 0
+ return 1;
+ }
+
+ public String getString(ByteBuffer bytes)
+ {
+ StringBuilder sb = new StringBuilder();
+ ByteBuffer bb = bytes.duplicate();
+ int i = 0;
+
+ while (bb.remaining() > 0)
+ {
+ if (bb.remaining() != bytes.remaining())
+ sb.append(":");
+
+ AbstractType comparator = getAndAppendNextComparator(i, bb, sb);
+ ByteBuffer value = getWithShortLength(bb);
+
+ sb.append(comparator.getString(value));
+
+ byte b = bb.get();
+ if (b != 0)
+ {
+ sb.append(":!");
+ break;
+ }
+ ++i;
+ }
+ return sb.toString();
+ }
+
+ /*
+ * FIXME: this would break if some of the component string representation
+ * contains ':'. None of our current comparator do so, so this is probably
+ * not an urgent matter, but this could break for custom comparator.
+ * (DynamicCompositeType would break on '@' too)
+ */
+ public ByteBuffer fromString(String source)
+ {
+ String[] parts = source.split(":");
+ List<ByteBuffer> components = new ArrayList<ByteBuffer>();
+ List<ParsedComparator> comparators = new ArrayList<ParsedComparator>();
+ int totalLength = 0, i = 0;
+ boolean lastByteIsOne = false;
+
+ for (String part : parts)
+ {
+ if (part.equals("!"))
+ {
+ lastByteIsOne = true;
+ break;
+ }
+
+ ParsedComparator p = parseNextComparator(i, part);
+ AbstractType type = p.getAbstractType();
+ part = p.getRemainingPart();
+
+ ByteBuffer component = type.fromString(part);
+ totalLength += p.getComparatorSerializedSize() + 2 +
component.remaining() + 1;
+ components.add(component);
+ comparators.add(p);
+ ++i;
+ }
+
+ ByteBuffer bb = ByteBuffer.allocate(totalLength);
+ i = 0;
+ for (ByteBuffer component : components)
+ {
+ comparators.get(i).serializeComparator(bb);
+ putShortLength(bb, component.remaining());
+ bb.put(component); // it's ok to consume component as we won't use
it anymore
+ bb.put((byte)0);
+ ++i;
+ }
+ if (lastByteIsOne)
+ bb.put(bb.limit() - 1, (byte)1);
+
+ bb.rewind();
+ return bb;
+ }
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ ByteBuffer bb = bytes.duplicate();
+
+ int i = 0;
+ while (bb.remaining() > 0)
+ {
+ AbstractType comparator = validateNextComparator(i, bb);
+
+ if (bb.remaining() < 2)
+ throw new MarshalException("Not enough bytes to read value
size of component " + i);
+ int length = getShortLength(bb);
+
+ if (bb.remaining() < length)
+ throw new MarshalException("Not enough bytes to read value of
component " + i);
+ ByteBuffer value = getBytes(bb, length);
+
+ comparator.validate(value);
+
+ if (bb.remaining() == 0)
+ throw new MarshalException("Not enough bytes to read the
end-of-component byte of component" + i);
+ byte b = bb.get();
+ if (b != 0 && bb.remaining() != 0)
+ throw new MarshalException("Invalid bytes remaining after an
end-of-component at component" + i);
+ ++i;
+ }
+ }
+
+ public ByteBuffer compose(ByteBuffer bytes)
+ {
+ return bytes;
+ }
+
+ public ByteBuffer decompose(ByteBuffer value)
+ {
+ return value;
+ }
+
+ public Class<ByteBuffer> getType()
+ {
+ return ByteBuffer.class;
+ }
+
+ public String toString(ByteBuffer value)
+ {
+ return getString(value);
+ }
+
+ /*
+ * JDBC metadata. For now we don't allow the use of compositeType with
+ * JDBC. We'll have to figure out what is the best solution here.
+ */
+ public boolean isSigned()
+ {
+ throw new UnsupportedOperationException("Not support for JDBC yet");
+ }
+
+ public boolean isCaseSensitive()
+ {
+ throw new UnsupportedOperationException("Not support for JDBC yet");
+ }
+
+ public boolean isCurrency()
+ {
+ throw new UnsupportedOperationException("Not support for JDBC yet");
+ }
+
+ public int getPrecision(ByteBuffer obj)
+ {
+ throw new UnsupportedOperationException("Not support for JDBC yet");
+ }
+
+ public int getScale(ByteBuffer obj)
+ {
+ throw new UnsupportedOperationException("Not support for JDBC yet");
+ }
+
+ public int getJdbcType()
+ {
+ throw new UnsupportedOperationException("Not support for JDBC yet");
+ }
+
+ public boolean needsQuotes()
+ {
+ throw new UnsupportedOperationException("Not support for JDBC yet");
+ }
+
+ abstract protected AbstractType getNextComparator(int i, ByteBuffer bb);
+ abstract protected AbstractType getNextComparator(int i, ByteBuffer bb1,
ByteBuffer bb2);
+ abstract protected AbstractType getAndAppendNextComparator(int i,
ByteBuffer bb, StringBuilder sb);
+ abstract protected ParsedComparator parseNextComparator(int i, String
part);
+ abstract protected AbstractType validateNextComparator(int i, ByteBuffer
bb) throws MarshalException;
+
+ protected static interface ParsedComparator
+ {
+ AbstractType getAbstractType();
+ String getRemainingPart();
+ int getComparatorSerializedSize();
+ void serializeComparator(ByteBuffer bb);
+ }
+}
Added:
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java?rev=1101120&view=auto
==============================================================================
---
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java
(added)
+++
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java
Mon May 9 17:24:34 2011
@@ -0,0 +1,144 @@
+/**
+ * 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.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+/*
+ * The encoding of a CompositeType column name should be:
+ * <component><component><component> ...
+ * where <component> is:
+ * <length of value><value><'end-of-component' byte>
+ * where <length of value> is a 2 bytes unsigned short the and the
+ * 'end-of-component' byte should always be 0 for actual column name.
+ * However, it can set to 1 for query bounds. This allows to query for the
+ * equivalent of 'give me the full super-column'. That is, if during a slice
+ * query uses:
+ * start = <3><"foo".getBytes()><0>
+ * end = <3><"foo".getBytes()><1>
+ * then he will be sure to get *all* the columns whose first component is
"foo".
+ * If for a component, the 'end-of-component' is != 0, there should not be any
+ * following component. The end-of-component can also be -1 to allow
+ * non-inclusive query. For instance:
+ * start = <3><"foo".getBytes()><-1>
+ * allows to query everything that is greater than <3><"foo".getBytes()>, but
+ * not <3><"foo".getBytes()> itself.
+ */
+public class CompositeType extends AbstractCompositeType
+{
+ // package protected for unit tests sake
+ final List<AbstractType> types;
+
+ // interning instances
+ private static final Map<List<AbstractType>, CompositeType> instances =
new HashMap<List<AbstractType>, CompositeType>();
+
+ public static CompositeType getInstance(TypeParser parser) throws
ConfigurationException
+ {
+ return getInstance(parser.getTypeParameters());
+ }
+
+ public static synchronized CompositeType getInstance(List<AbstractType>
types) throws ConfigurationException
+ {
+ if (types == null || types.isEmpty())
+ throw new ConfigurationException("Nonsensical empty parameter list
for CompositeType");
+
+ CompositeType ct = instances.get(types);
+ if (ct == null)
+ {
+ ct = new CompositeType(types);
+ instances.put(types, ct);
+ }
+ return ct;
+ }
+
+ private CompositeType(List<AbstractType> types)
+ {
+ this.types = types;
+ }
+
+ protected AbstractType getNextComparator(int i, ByteBuffer bb)
+ {
+ return types.get(i);
+ }
+
+ protected AbstractType getNextComparator(int i, ByteBuffer bb1, ByteBuffer
bb2)
+ {
+ return types.get(i);
+ }
+
+ protected AbstractType getAndAppendNextComparator(int i, ByteBuffer bb,
StringBuilder sb)
+ {
+ return types.get(i);
+ }
+
+ protected ParsedComparator parseNextComparator(int i, String part)
+ {
+ return new StaticParsedComparator(types.get(i), part);
+ }
+
+ protected AbstractType validateNextComparator(int i, ByteBuffer bb) throws
MarshalException
+ {
+ if (i >= types.size())
+ throw new MarshalException("Too many bytes for comparator");
+ return types.get(i);
+ }
+
+ private static class StaticParsedComparator implements ParsedComparator
+ {
+ final AbstractType type;
+ final String part;
+
+ StaticParsedComparator(AbstractType type, String part)
+ {
+ this.type = type;
+ this.part = part;
+ }
+
+ public AbstractType getAbstractType()
+ {
+ return type;
+ }
+
+ public String getRemainingPart()
+ {
+ return part;
+ }
+
+ public int getComparatorSerializedSize()
+ {
+ return 0;
+ }
+
+ public void serializeComparator(ByteBuffer bb) {}
+ }
+
+ @Override
+ public String toString()
+ {
+ return getClass().getName() +
TypeParser.stringifyTypeParameters(types);
+ }
+}
Added:
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java?rev=1101120&view=auto
==============================================================================
---
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java
(added)
+++
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java
Mon May 9 17:24:34 2011
@@ -0,0 +1,266 @@
+/**
+ * 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.charset.CharacterCodingException;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.FBUtilities;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+ * The encoding of a DynamicCompositeType column name should be:
+ * <component><component><component> ...
+ * where <component> is:
+ * <comparator part><value><'end-of-component' byte>
+ * where:
+ * - <comparator part>: either the comparator full name, or a declared
+ * aliases. This is at least 2 bytes (those 2 bytes are called header in
+ * the following). If the first bit of the header is 1, then this
+ * comparator part is an alias, otherwise it's a comparator full name:
+ * - aliases: the actual alias is the 2nd byte of header taken as a
+ * character. The whole <comparator part> is thus 2 byte long.
+ * - comparator full name: the header is the length of the remaining
+ * part. The remaining part is the UTF-8 encoded comparator class
+ * name.
+ * - <value>: the component value bytes preceded by 2 byte containing the
+ * size of value (see CompositeType).
+ * - 'end-of-component' byte is defined as in CompositeType
+ */
+public class DynamicCompositeType extends AbstractCompositeType
+{
+ private static final Logger logger =
LoggerFactory.getLogger(DynamicCompositeType.class);
+
+ private final Map<Byte, AbstractType> aliases;
+
+ // interning instances
+ private static final Map<Map<Byte, AbstractType>, DynamicCompositeType>
instances = new HashMap<Map<Byte, AbstractType>, DynamicCompositeType>();
+
+ public static synchronized DynamicCompositeType getInstance(TypeParser
parser) throws ConfigurationException
+ {
+ return getInstance(parser.getAliasParameters());
+ }
+
+ public static synchronized DynamicCompositeType getInstance(Map<Byte,
AbstractType> aliases)
+ {
+ DynamicCompositeType dct = instances.get(aliases);
+ if (dct == null)
+ {
+ dct = new DynamicCompositeType(aliases);
+ instances.put(aliases, dct);
+ }
+ return dct;
+ }
+
+ private DynamicCompositeType(Map<Byte, AbstractType> aliases)
+ {
+ this.aliases = aliases;
+ }
+
+ private AbstractType getComparator(ByteBuffer bb)
+ {
+ try
+ {
+ int header = getShortLength(bb);
+ if ((header & 0x8000) == 0)
+ {
+ String name = ByteBufferUtil.string(getBytes(bb, header));
+ return TypeParser.parse(name);
+ }
+ else
+ {
+ return aliases.get((byte)(header & 0xFF));
+ }
+ }
+ catch (CharacterCodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ catch (ConfigurationException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected AbstractType getNextComparator(int i, ByteBuffer bb)
+ {
+ return getComparator(bb);
+ }
+
+ protected AbstractType getNextComparator(int i, ByteBuffer bb1, ByteBuffer
bb2)
+ {
+ AbstractType comp1 = getComparator(bb1);
+ AbstractType comp2 = getComparator(bb2);
+
+ // This rely on comparator always being singleton instances
+ if (comp1 != comp2)
+ {
+ logger.error("Mismatch between {} and {}", comp1, comp2);
+ throw new RuntimeException("Comparator mismatch while comparing
two DynamicCompositeType colum name");
+ }
+ return comp1;
+ }
+
+ protected AbstractType getAndAppendNextComparator(int i, ByteBuffer bb,
StringBuilder sb)
+ {
+ try
+ {
+ int header = getShortLength(bb);
+ if ((header & 0x8000) == 0)
+ {
+ String name = ByteBufferUtil.string(getBytes(bb, header));
+ sb.append(name).append("@");
+ return TypeParser.parse(name);
+ }
+ else
+ {
+ sb.append((char)(header & 0xFF)).append("@");
+ return aliases.get((byte)(header & 0xFF));
+ }
+ }
+ catch (CharacterCodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ catch (ConfigurationException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected ParsedComparator parseNextComparator(int i, String part)
+ {
+ return new DynamicParsedComparator(part);
+ }
+
+ protected AbstractType validateNextComparator(int i, ByteBuffer bb) throws
MarshalException
+ {
+ AbstractType comparator = null;
+ if (bb.remaining() < 2)
+ throw new MarshalException("Not enough bytes to header of the
comparator part of component " + i);
+ int header = getShortLength(bb);
+ if ((header & 0x8000) == 0)
+ {
+ if (bb.remaining() < header)
+ throw new MarshalException("Not enough bytes to read
comparator name of component " + i);
+
+ ByteBuffer value = getBytes(bb, header);
+ try
+ {
+ comparator = TypeParser.parse(ByteBufferUtil.string(value));
+ }
+ catch (Exception e)
+ {
+ // we'll deal with this below since comparator == null
+ }
+ }
+ else
+ {
+ comparator = aliases.get((byte)(header & 0xFF));
+ }
+
+ if (comparator == null)
+ throw new MarshalException("Cannot find comparator for component "
+ i);
+ else
+ return comparator;
+ }
+
+ private class DynamicParsedComparator implements ParsedComparator
+ {
+ final AbstractType type;
+ final boolean isAlias;
+ final String comparatorName;
+ final String remainingPart;
+
+ DynamicParsedComparator(String part)
+ {
+ String[] splits = part.split("@");
+ if (splits.length != 2)
+ throw new IllegalArgumentException("Invalid component
representation: " + part);
+
+ comparatorName = splits[0];
+ remainingPart = splits[1];
+
+ try
+ {
+ AbstractType t = null;
+ if (comparatorName.length() == 1)
+ {
+ // try for an alias
+ // Note: the char to byte cast is theorically bogus for
unicode character. I take full
+ // responsibility if someone get hit by this (without
making it on purpose)
+ t = aliases.get((byte)comparatorName.charAt(0));
+ }
+ isAlias = t != null;
+ if (!isAlias)
+ {
+ t = TypeParser.parse(comparatorName);
+ }
+ type = t;
+ }
+ catch (ConfigurationException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public AbstractType getAbstractType()
+ {
+ return type;
+ }
+
+ public String getRemainingPart()
+ {
+ return remainingPart;
+ }
+
+ public int getComparatorSerializedSize()
+ {
+ return isAlias ? 2 : 2 +
ByteBufferUtil.bytes(comparatorName).remaining();
+ }
+
+ public void serializeComparator(ByteBuffer bb)
+ {
+ int header = 0;
+ if (isAlias)
+ header = 0x8000 | ((byte)comparatorName.charAt(0));
+ else
+ header = comparatorName.length();
+ putShortLength(bb, header);
+
+ if (!isAlias)
+ bb.put(ByteBufferUtil.bytes(comparatorName));
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return getClass().getName() +
TypeParser.stringifyAliasesParameters(aliases);
+ }
+}
Modified:
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java?rev=1101120&r1=1101119&r2=1101120&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java
(original)
+++
cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java
Mon May 9 17:24:34 2011
@@ -27,6 +27,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import org.apache.commons.lang.StringUtils;
+
import org.apache.cassandra.config.ConfigurationException;
import org.apache.cassandra.utils.FBUtilities;
@@ -414,17 +416,7 @@ public class TypeParser
public static String stringifyTypeParameters(List<AbstractType> types)
{
StringBuilder sb = new StringBuilder();
- sb.append('(');
- Iterator<AbstractType> iter = types.iterator();
- if (iter.hasNext())
- {
- sb.append(iter.next());
- }
- while (iter.hasNext())
- {
- sb.append(',').append(iter.next());
- }
- sb.append(')');
+ sb.append('(').append(StringUtils.join(types, ",")).append(')');
return sb.toString();
}
}
Modified:
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java?rev=1101120&r1=1101119&r2=1101120&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java
(original)
+++
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java
Mon May 9 17:24:34 2011
@@ -54,7 +54,7 @@ public class SchemaLoader
}
}
- public static Collection<KSMetaData> schemaDefinition()
+ public static Collection<KSMetaData> schemaDefinition() throws
ConfigurationException
{
List<KSMetaData> schema = new ArrayList<KSMetaData>();
@@ -78,6 +78,12 @@ public class SchemaLoader
ColumnFamilyType st = ColumnFamilyType.Standard;
ColumnFamilyType su = ColumnFamilyType.Super;
AbstractType bytes = BytesType.instance;
+
+ AbstractType composite = CompositeType.getInstance(Arrays.asList(new
AbstractType[]{BytesType.instance, TimeUUIDType.instance,
IntegerType.instance}));
+ Map<Byte, AbstractType> aliases = new HashMap<Byte, AbstractType>();
+ aliases.put((byte)'b', BytesType.instance);
+ aliases.put((byte)'t', TimeUUIDType.instance);
+ AbstractType dynamicComposite =
DynamicCompositeType.getInstance(aliases);
// these column definitions will will be applied to the jdbc utf and
integer column familes respectively.
Map<ByteBuffer, ColumnDefinition> integerColumn = new
HashMap<ByteBuffer, ColumnDefinition>();
@@ -134,7 +140,17 @@ public class SchemaLoader
jdbcCFMD(ks1, "JdbcUtf8",
UTF8Type.instance).columnMetadata(utf8Column),
jdbcCFMD(ks1, "JdbcLong", LongType.instance),
jdbcCFMD(ks1, "JdbcBytes", bytes),
- jdbcCFMD(ks1, "JdbcAscii",
AsciiType.instance)));
+ jdbcCFMD(ks1, "JdbcAscii",
AsciiType.instance),
+ new CFMetaData(ks1,
+ "StandardComposite",
+ st,
+ composite,
+ null),
+ new CFMetaData(ks1,
+ "StandardDynamicComposite",
+ st,
+ dynamicComposite,
+ null)));
// Keyspace 2
schema.add(new KSMetaData(ks2,
Added:
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java?rev=1101120&view=auto
==============================================================================
---
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
(added)
+++
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
Mon May 9 17:24:34 2011
@@ -0,0 +1,268 @@
+/*
+* 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.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+import org.apache.cassandra.CleanupHelper;
+import org.apache.cassandra.Util;
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.columniterator.IdentityQueryFilter;
+import org.apache.cassandra.db.filter.QueryFilter;
+import org.apache.cassandra.db.filter.QueryPath;
+import org.apache.cassandra.utils.*;
+
+public class CompositeTypeTest extends CleanupHelper
+{
+ private static final String cfName = "StandardComposite";
+ private static final CompositeType comparator;
+ static
+ {
+ List<AbstractType> subComparators = new ArrayList<AbstractType>();
+ subComparators.add(BytesType.instance);
+ subComparators.add(TimeUUIDType.instance);
+ subComparators.add(IntegerType.instance);
+ try
+ {
+ comparator = CompositeType.getInstance(subComparators);
+ }
+ catch (ConfigurationException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private static final int UUID_COUNT = 3;
+ private static final UUID[] uuids = new UUID[UUID_COUNT];
+ static
+ {
+ for (int i = 0; i < UUID_COUNT; ++i)
+ uuids[i] =
UUIDGen.makeType1UUIDFromHost(FBUtilities.getLocalAddress());
+ }
+
+ @Test
+ public void testEndOfComponent()
+ {
+ ByteBuffer[] cnames = {
+ createCompositeKey("test1", uuids[0], -1, false),
+ createCompositeKey("test1", uuids[1], 24, false),
+ createCompositeKey("test1", uuids[1], 42, false),
+ createCompositeKey("test1", uuids[1], 83, false),
+ createCompositeKey("test1", uuids[2], -1, false),
+ createCompositeKey("test1", uuids[2], 42, false),
+ };
+
+ ByteBuffer start = createCompositeKey("test1", uuids[1], -1, false);
+ ByteBuffer stop = createCompositeKey("test1", uuids[1], -1, true);
+
+ for (int i = 0; i < 1; ++i)
+ {
+ assert comparator.compare(start, cnames[i]) > 0;
+ assert comparator.compare(stop, cnames[i]) > 0;
+ }
+ for (int i = 1; i < 4; ++i)
+ {
+ assert comparator.compare(start, cnames[i]) < 0;
+ assert comparator.compare(stop, cnames[i]) > 0;
+ }
+ for (int i = 4; i < cnames.length; ++i)
+ {
+ assert comparator.compare(start, cnames[i]) < 0;
+ assert comparator.compare(stop, cnames[i]) < 0;
+ }
+ }
+
+ @Test
+ public void testGetString()
+ {
+ String test1Hex =
ByteBufferUtil.bytesToHex(ByteBufferUtil.bytes("test1"));
+ ByteBuffer key = createCompositeKey("test1", uuids[1], 42, false);
+ assert comparator.getString(key).equals(test1Hex + ":" + uuids[1] +
":42");
+
+ key = createCompositeKey("test1", uuids[1], -1, true);
+ assert comparator.getString(key).equals(test1Hex + ":" + uuids[1] +
":!");
+ }
+
+ @Test
+ public void testFromString()
+ {
+ String test1Hex =
ByteBufferUtil.bytesToHex(ByteBufferUtil.bytes("test1"));
+ ByteBuffer key = createCompositeKey("test1", uuids[1], 42, false);
+ assert key.equals(comparator.fromString(test1Hex + ":" + uuids[1] +
":42"));
+
+ key = createCompositeKey("test1", uuids[1], -1, true);
+ assert key.equals(comparator.fromString(test1Hex + ":" + uuids[1] +
":!"));
+ }
+
+ @Test
+ public void testValidate()
+ {
+ ByteBuffer key = createCompositeKey("test1", uuids[1], 42, false);
+ comparator.validate(key);
+
+ key = createCompositeKey("test1", null, -1, false);
+ comparator.validate(key);
+
+ key = createCompositeKey("test1", uuids[2], -1, true);
+ comparator.validate(key);
+
+ key.get(); // make sure we're not aligned anymore
+ try
+ {
+ comparator.validate(key);
+ fail("Should not validate");
+ }
+ catch (MarshalException e) {}
+
+ key = ByteBuffer.allocate(3 + "test1".length() + 3 + 14);
+ key.putShort((short) "test1".length());
+ key.put(ByteBufferUtil.bytes("test1"));
+ key.put((byte) 0);
+ key.putShort((short) 14);
+ key.rewind();
+ try
+ {
+ comparator.validate(key);
+ fail("Should not validate");
+ }
+ catch (MarshalException e)
+ {
+ assert e.toString().contains("TimeUUID should be 16 or 0 bytes");
+ }
+
+ key = createCompositeKey("test1", UUID.randomUUID(), 42, false);
+ try
+ {
+ comparator.validate(key);
+ fail("Should not validate");
+ }
+ catch (MarshalException e)
+ {
+ assert e.toString().contains("Invalid version for TimeUUID type");
+ }
+ }
+
+ @Test
+ public void testFullRound() throws Exception
+ {
+ Table table = Table.open("Keyspace1");
+ ColumnFamilyStore cfs = table.getColumnFamilyStore(cfName);
+
+ ByteBuffer cname1 = createCompositeKey("test1", null, -1, false);
+ ByteBuffer cname2 = createCompositeKey("test1", uuids[0], 24, false);
+ ByteBuffer cname3 = createCompositeKey("test1", uuids[0], 42, false);
+ ByteBuffer cname4 = createCompositeKey("test2", uuids[0], -1, false);
+ ByteBuffer cname5 = createCompositeKey("test2", uuids[1], 42, false);
+
+ ByteBuffer key = ByteBufferUtil.bytes("k");
+ RowMutation rm = new RowMutation("Keyspace1", key);
+ addColumn(rm, cname5);
+ addColumn(rm, cname1);
+ addColumn(rm, cname4);
+ addColumn(rm, cname2);
+ addColumn(rm, cname3);
+ rm.apply();
+
+ ColumnFamily cf =
cfs.getColumnFamily(QueryFilter.getIdentityFilter(Util.dk("k"), new
QueryPath(cfName, null, null)));
+
+ Iterator<IColumn> iter = cf.getSortedColumns().iterator();
+
+ assert iter.next().name().equals(cname1);
+ assert iter.next().name().equals(cname2);
+ assert iter.next().name().equals(cname3);
+ assert iter.next().name().equals(cname4);
+ assert iter.next().name().equals(cname5);
+ }
+
+ @Test
+ public void testEmptyParametersNotallowed()
+ {
+ try
+ {
+ TypeParser.parse("CompositeType");
+ fail("Shouldn't work");
+ }
+ catch (ConfigurationException e) {}
+
+ try
+ {
+ TypeParser.parse("CompositeType()");
+ fail("Shouldn't work");
+ }
+ catch (ConfigurationException e) {}
+ }
+
+
+ private void addColumn(RowMutation rm, ByteBuffer cname)
+ {
+ rm.add(new QueryPath(cfName, null , cname),
ByteBufferUtil.EMPTY_BYTE_BUFFER, 0);
+ }
+
+ private ByteBuffer createCompositeKey(String s, UUID uuid, int i, boolean
lastIsOne)
+ {
+ ByteBuffer bytes = ByteBufferUtil.bytes(s);
+ int totalSize = 0;
+ if (s != null)
+ {
+ totalSize += 2 + bytes.remaining() + 1;
+ if (uuid != null)
+ {
+ totalSize += 2 + 16 + 1;
+ if (i != -1)
+ {
+ totalSize += 2 + 1 + 1;
+ }
+ }
+ }
+
+ ByteBuffer bb = ByteBuffer.allocate(totalSize);
+
+ if (s != null)
+ {
+ bb.putShort((short) bytes.remaining());
+ bb.put(bytes);
+ bb.put(uuid == null && lastIsOne ? (byte)1 : (byte)0);
+ if (uuid != null)
+ {
+ bb.putShort((short) 16);
+ bb.put(UUIDGen.decompose(uuid));
+ bb.put(i == -1 && lastIsOne ? (byte)1 : (byte)0);
+ if (i != -1)
+ {
+ // We are putting a byte only because our test use ints
that fit in a byte *and* IntegerType.fromString() will
+ // return something compatible (i.e, putting a full int
here would break 'fromStringTest')
+ bb.putShort((short) 1);
+ bb.put((byte)i);
+ bb.put(lastIsOne ? (byte)1 : (byte)0);
+ }
+ }
+ }
+ bb.rewind();
+ return bb;
+ }
+}
Added:
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java?rev=1101120&view=auto
==============================================================================
---
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java
(added)
+++
cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java
Mon May 9 17:24:34 2011
@@ -0,0 +1,246 @@
+/*
+* 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.Iterator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+import org.apache.cassandra.CleanupHelper;
+import org.apache.cassandra.Util;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.columniterator.IdentityQueryFilter;
+import org.apache.cassandra.db.filter.QueryFilter;
+import org.apache.cassandra.db.filter.QueryPath;
+import org.apache.cassandra.utils.*;
+
+public class DynamicCompositeTypeTest extends CleanupHelper
+{
+ private static final String cfName = "StandardDynamicComposite";
+
+ private static final DynamicCompositeType comparator;
+ static
+ {
+ Map<Byte, AbstractType> aliases = new HashMap<Byte, AbstractType>();
+ aliases.put((byte)'b', BytesType.instance);
+ aliases.put((byte)'t', TimeUUIDType.instance);
+ comparator = DynamicCompositeType.getInstance(aliases);
+ }
+
+ private static final int UUID_COUNT = 3;
+ private static final UUID[] uuids = new UUID[UUID_COUNT];
+ static
+ {
+ for (int i = 0; i < UUID_COUNT; ++i)
+ uuids[i] =
UUIDGen.makeType1UUIDFromHost(FBUtilities.getLocalAddress());
+ }
+
+ @Test
+ public void testEndOfComponent()
+ {
+ ByteBuffer[] cnames = {
+ createDynamicCompositeKey("test1", uuids[0], -1, false),
+ createDynamicCompositeKey("test1", uuids[1], 24, false),
+ createDynamicCompositeKey("test1", uuids[1], 42, false),
+ createDynamicCompositeKey("test1", uuids[1], 83, false),
+ createDynamicCompositeKey("test1", uuids[2], -1, false),
+ createDynamicCompositeKey("test1", uuids[2], 42, false),
+ };
+
+ ByteBuffer start = createDynamicCompositeKey("test1", uuids[1], -1,
false);
+ ByteBuffer stop = createDynamicCompositeKey("test1", uuids[1], -1,
true);
+
+ for (int i = 0; i < 1; ++i)
+ {
+ assert comparator.compare(start, cnames[i]) > 0;
+ assert comparator.compare(stop, cnames[i]) > 0;
+ }
+ for (int i = 1; i < 4; ++i)
+ {
+ assert comparator.compare(start, cnames[i]) < 0;
+ assert comparator.compare(stop, cnames[i]) > 0;
+ }
+ for (int i = 4; i < cnames.length; ++i)
+ {
+ assert comparator.compare(start, cnames[i]) < 0;
+ assert comparator.compare(stop, cnames[i]) < 0;
+ }
+ }
+
+ @Test
+ public void testGetString()
+ {
+ String test1Hex =
ByteBufferUtil.bytesToHex(ByteBufferUtil.bytes("test1"));
+ ByteBuffer key = createDynamicCompositeKey("test1", uuids[1], 42,
false);
+ assert comparator.getString(key).equals("b@" + test1Hex + ":t@" +
uuids[1] + ":IntegerType@42");
+
+ key = createDynamicCompositeKey("test1", uuids[1], -1, true);
+ assert comparator.getString(key).equals("b@" + test1Hex + ":t@" +
uuids[1] + ":!");
+ }
+
+ @Test
+ public void testFromString()
+ {
+ String test1Hex =
ByteBufferUtil.bytesToHex(ByteBufferUtil.bytes("test1"));
+ ByteBuffer key = createDynamicCompositeKey("test1", uuids[1], 42,
false);
+ assert key.equals(comparator.fromString("b@" + test1Hex + ":t@" +
uuids[1] + ":IntegerType@42"));
+
+ key = createDynamicCompositeKey("test1", uuids[1], -1, true);
+ assert key.equals(comparator.fromString("b@" + test1Hex + ":t@" +
uuids[1] + ":!"));
+ }
+
+ @Test
+ public void testValidate()
+ {
+ ByteBuffer key = createDynamicCompositeKey("test1", uuids[1], 42,
false);
+ comparator.validate(key);
+
+ key = createDynamicCompositeKey("test1", null, -1, false);
+ comparator.validate(key);
+
+ key = createDynamicCompositeKey("test1", uuids[2], -1, true);
+ comparator.validate(key);
+
+ key.get(); // make sure we're not aligned anymore
+ try
+ {
+ comparator.validate(key);
+ fail("Should not validate");
+ }
+ catch (MarshalException e) {}
+
+ key = ByteBuffer.allocate(5 + "test1".length() + 5 + 14);
+ key.putShort((short) (0x8000 | 'b'));
+ key.putShort((short) "test1".length());
+ key.put(ByteBufferUtil.bytes("test1"));
+ key.put((byte) 0);
+ key.putShort((short) (0x8000 | 't'));
+ key.putShort((short) 14);
+ key.rewind();
+ try
+ {
+ comparator.validate(key);
+ fail("Should not validate");
+ }
+ catch (MarshalException e)
+ {
+ assert e.toString().contains("TimeUUID should be 16 or 0 bytes");
+ }
+
+ key = createDynamicCompositeKey("test1", UUID.randomUUID(), 42, false);
+ try
+ {
+ comparator.validate(key);
+ fail("Should not validate");
+ }
+ catch (MarshalException e)
+ {
+ assert e.toString().contains("Invalid version for TimeUUID type");
+ }
+ }
+
+ @Test
+ public void testFullRound() throws Exception
+ {
+ Table table = Table.open("Keyspace1");
+ ColumnFamilyStore cfs = table.getColumnFamilyStore(cfName);
+
+ ByteBuffer cname1 = createDynamicCompositeKey("test1", null, -1,
false);
+ ByteBuffer cname2 = createDynamicCompositeKey("test1", uuids[0], 24,
false);
+ ByteBuffer cname3 = createDynamicCompositeKey("test1", uuids[0], 42,
false);
+ ByteBuffer cname4 = createDynamicCompositeKey("test2", uuids[0], -1,
false);
+ ByteBuffer cname5 = createDynamicCompositeKey("test2", uuids[1], 42,
false);
+
+ ByteBuffer key = ByteBufferUtil.bytes("k");
+ RowMutation rm = new RowMutation("Keyspace1", key);
+ addColumn(rm, cname5);
+ addColumn(rm, cname1);
+ addColumn(rm, cname4);
+ addColumn(rm, cname2);
+ addColumn(rm, cname3);
+ rm.apply();
+
+ ColumnFamily cf =
cfs.getColumnFamily(QueryFilter.getIdentityFilter(Util.dk("k"), new
QueryPath(cfName, null, null)));
+
+ Iterator<IColumn> iter = cf.getSortedColumns().iterator();
+
+ assert iter.next().name().equals(cname1);
+ assert iter.next().name().equals(cname2);
+ assert iter.next().name().equals(cname3);
+ assert iter.next().name().equals(cname4);
+ assert iter.next().name().equals(cname5);
+ }
+
+ private void addColumn(RowMutation rm, ByteBuffer cname)
+ {
+ rm.add(new QueryPath(cfName, null , cname),
ByteBufferUtil.EMPTY_BYTE_BUFFER, 0);
+ }
+
+ private ByteBuffer createDynamicCompositeKey(String s, UUID uuid, int i,
boolean lastIsOne)
+ {
+ ByteBuffer bytes = ByteBufferUtil.bytes(s);
+ int totalSize = 0;
+ if (s != null)
+ {
+ totalSize += 2 + 2 + bytes.remaining() + 1;
+ if (uuid != null)
+ {
+ totalSize += 2 + 2 + 16 + 1;
+ if (i != -1)
+ {
+ totalSize += 2 + "IntegerType".length() + 2 + 1 + 1;
+ }
+ }
+ }
+
+ ByteBuffer bb = ByteBuffer.allocate(totalSize);
+
+ if (s != null)
+ {
+ bb.putShort((short)(0x8000 | 'b'));
+ bb.putShort((short) bytes.remaining());
+ bb.put(bytes);
+ bb.put(uuid == null && lastIsOne ? (byte)1 : (byte)0);
+ if (uuid != null)
+ {
+ bb.putShort((short)(0x8000 | 't'));
+ bb.putShort((short) 16);
+ bb.put(UUIDGen.decompose(uuid));
+ bb.put(i == -1 && lastIsOne ? (byte)1 : (byte)0);
+ if (i != -1)
+ {
+ bb.putShort((short) "IntegerType".length());
+ bb.put(ByteBufferUtil.bytes("IntegerType"));
+ // We are putting a byte only because our test use ints
that fit in a byte *and* IntegerType.fromString() will
+ // return something compatible (i.e, putting a full int
here would break 'fromStringTest')
+ bb.putShort((short) 1);
+ bb.put((byte)i);
+ bb.put(lastIsOne ? (byte)1 : (byte)0);
+ }
+ }
+ }
+ bb.rewind();
+ return bb;
+ }
+}