You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ty...@apache.org on 2014/11/11 20:33:45 UTC
[1/4] cassandra git commit: Support for frozen collections
Repository: cassandra
Updated Branches:
refs/heads/trunk f2e2862f5 -> fb4356a36
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/marshal/TypeParser.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TypeParser.java b/src/java/org/apache/cassandra/db/marshal/TypeParser.java
index 1b83180..cdb5679 100644
--- a/src/java/org/apache/cassandra/db/marshal/TypeParser.java
+++ b/src/java/org/apache/cassandra/db/marshal/TypeParser.java
@@ -241,7 +241,7 @@ public class TypeParser
public Map<ByteBuffer, CollectionType> getCollectionsParameters() throws SyntaxException, ConfigurationException
{
- Map<ByteBuffer, CollectionType> map = new HashMap<ByteBuffer, CollectionType>();
+ Map<ByteBuffer, CollectionType> map = new HashMap<>();
if (isEOS())
return map;
@@ -539,25 +539,37 @@ public class TypeParser
*/
public static String stringifyTypeParameters(List<AbstractType<?>> types)
{
- StringBuilder sb = new StringBuilder();
- sb.append('(').append(StringUtils.join(types, ",")).append(')');
- return sb.toString();
+ return stringifyTypeParameters(types, false);
+ }
+
+ /**
+ * Helper function to ease the writing of AbstractType.toString() methods.
+ */
+ public static String stringifyTypeParameters(List<AbstractType<?>> types, boolean ignoreFreezing)
+ {
+ StringBuilder sb = new StringBuilder("(");
+ for (int i = 0; i < types.size(); i++)
+ {
+ if (i > 0)
+ sb.append(",");
+ sb.append(types.get(i).toString(ignoreFreezing));
+ }
+ return sb.append(')').toString();
}
- public static String stringifyCollectionsParameters(Map<ByteBuffer, CollectionType> collections)
+ public static String stringifyCollectionsParameters(Map<ByteBuffer, ? extends CollectionType> collections)
{
StringBuilder sb = new StringBuilder();
sb.append('(');
boolean first = true;
- for (Map.Entry<ByteBuffer, CollectionType> entry : collections.entrySet())
+ for (Map.Entry<ByteBuffer, ? extends CollectionType> entry : collections.entrySet())
{
if (!first)
- {
sb.append(',');
- }
+
first = false;
sb.append(ByteBufferUtil.bytesToHex(entry.getKey())).append(":");
- entry.getValue().appendToStringBuilder(sb);
+ sb.append(entry.getValue());
}
sb.append(')');
return sb.toString();
@@ -572,7 +584,9 @@ public class TypeParser
{
sb.append(',');
sb.append(ByteBufferUtil.bytesToHex(columnNames.get(i))).append(":");
- sb.append(columnTypes.get(i).toString());
+
+ // omit FrozenType(...) from fields because it is currently implicit
+ sb.append(columnTypes.get(i).toString(true));
}
sb.append(')');
return sb.toString();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/UserType.java b/src/java/org/apache/cassandra/db/marshal/UserType.java
index 44c208f..180d713 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -61,7 +61,7 @@ public class UserType extends TupleType
for (Pair<ByteBuffer, AbstractType> p : params.right)
{
columnNames.add(p.left);
- columnTypes.add(p.right);
+ columnTypes.add(p.right.freeze());
}
return new UserType(keyspace, name, columnNames, columnTypes);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/hadoop/pig/CqlStorage.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/hadoop/pig/CqlStorage.java b/src/java/org/apache/cassandra/hadoop/pig/CqlStorage.java
index 7a6be71..2ba4dbf 100644
--- a/src/java/org/apache/cassandra/hadoop/pig/CqlStorage.java
+++ b/src/java/org/apache/cassandra/hadoop/pig/CqlStorage.java
@@ -148,9 +148,9 @@ public class CqlStorage extends AbstractCassandraStorage
}
AbstractType elementValidator;
if (validator instanceof SetType)
- elementValidator = ((SetType<?>) validator).elements;
+ elementValidator = ((SetType<?>) validator).getElementsType();
else if (validator instanceof ListType)
- elementValidator = ((ListType<?>) validator).elements;
+ elementValidator = ((ListType<?>) validator).getElementsType();
else
return;
@@ -167,8 +167,8 @@ public class CqlStorage extends AbstractCassandraStorage
/** set the values of set/list at and after the position of the tuple */
private void setMapTupleValues(Tuple tuple, int position, Object value, AbstractType<?> validator) throws ExecException
{
- AbstractType<?> keyValidator = ((MapType<?, ?>) validator).keys;
- AbstractType<?> valueValidator = ((MapType<?, ?>) validator).values;
+ AbstractType<?> keyValidator = ((MapType<?, ?>) validator).getKeysType();
+ AbstractType<?> valueValidator = ((MapType<?, ?>) validator).getValuesType();
int i = 0;
Tuple innerTuple = TupleFactory.getInstance().newTuple(((Map<?,?>) value).size());
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/serializers/CollectionSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/CollectionSerializer.java b/src/java/org/apache/cassandra/serializers/CollectionSerializer.java
index 2a5e809..29ae2fd 100644
--- a/src/java/org/apache/cassandra/serializers/CollectionSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/CollectionSerializer.java
@@ -19,8 +19,10 @@
package org.apache.cassandra.serializers;
import java.nio.ByteBuffer;
+import java.util.Collection;
import java.util.List;
+import org.apache.cassandra.transport.Server;
import org.apache.cassandra.utils.ByteBufferUtil;
public abstract class CollectionSerializer<T> implements TypeSerializer<T>
@@ -35,17 +37,17 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T>
{
List<ByteBuffer> values = serializeValues(value);
// See deserialize() for why using the protocol v3 variant is the right thing to do.
- return pack(values, getElementCount(value), 3);
+ return pack(values, getElementCount(value), Server.VERSION_3);
}
public T deserialize(ByteBuffer bytes)
{
// The only cases we serialize/deserialize collections internally (i.e. not for the protocol sake),
// is:
- // 1) when collections are in UDT values
+ // 1) when collections are frozen
// 2) for internal calls.
// In both case, using the protocol 3 version variant is the right thing to do.
- return deserializeForNativeProtocol(bytes, 3);
+ return deserializeForNativeProtocol(bytes, Server.VERSION_3);
}
public ByteBuffer reserializeToV3(ByteBuffer bytes)
@@ -55,11 +57,11 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T>
public void validate(ByteBuffer bytes) throws MarshalException
{
- // Same thing than above
- validateForNativeProtocol(bytes, 3);
+ // Same thing as above
+ validateForNativeProtocol(bytes, Server.VERSION_3);
}
- public static ByteBuffer pack(List<ByteBuffer> buffers, int elements, int version)
+ public static ByteBuffer pack(Collection<ByteBuffer> buffers, int elements, int version)
{
int size = 0;
for (ByteBuffer bb : buffers)
@@ -74,7 +76,7 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T>
protected static void writeCollectionSize(ByteBuffer output, int elements, int version)
{
- if (version >= 3)
+ if (version >= Server.VERSION_3)
output.putInt(elements);
else
output.putShort((short)elements);
@@ -82,12 +84,12 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T>
public static int readCollectionSize(ByteBuffer input, int version)
{
- return version >= 3 ? input.getInt() : ByteBufferUtil.readShortLength(input);
+ return version >= Server.VERSION_3 ? input.getInt() : ByteBufferUtil.readShortLength(input);
}
protected static int sizeOfCollectionSize(int elements, int version)
{
- return version >= 3 ? 4 : 2;
+ return version >= Server.VERSION_3 ? 4 : 2;
}
protected static void writeValue(ByteBuffer output, ByteBuffer value, int version)
@@ -113,7 +115,7 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T>
public static ByteBuffer readValue(ByteBuffer input, int version)
{
- if (version >= 3)
+ if (version >= Server.VERSION_3)
{
int size = input.getInt();
if (size < 0)
@@ -129,7 +131,7 @@ public abstract class CollectionSerializer<T> implements TypeSerializer<T>
protected static int sizeOfValue(ByteBuffer value, int version)
{
- if (version >= 3)
+ if (version >= Server.VERSION_3)
{
return value == null ? 4 : 4 + value.remaining();
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/serializers/ListSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/ListSerializer.java b/src/java/org/apache/cassandra/serializers/ListSerializer.java
index 7387e1b..aeee2b9 100644
--- a/src/java/org/apache/cassandra/serializers/ListSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/ListSerializer.java
@@ -18,12 +18,12 @@
package org.apache.cassandra.serializers;
+import org.apache.cassandra.transport.Server;
+
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.*;
-import org.apache.cassandra.utils.ByteBufferUtil;
-
public class ListSerializer<T> extends CollectionSerializer<List<T>>
{
// interning instances
@@ -111,6 +111,34 @@ public class ListSerializer<T> extends CollectionSerializer<List<T>>
}
}
+ /**
+ * Returns the element at the given index in a list.
+ * @param serializedList a serialized list
+ * @param index the index to get
+ * @return the serialized element at the given index, or null if the index exceeds the list size
+ */
+ public ByteBuffer getElement(ByteBuffer serializedList, int index)
+ {
+ try
+ {
+ ByteBuffer input = serializedList.duplicate();
+ int n = readCollectionSize(input, Server.VERSION_3);
+ if (n <= index)
+ return null;
+
+ for (int i = 0; i < index; i++)
+ {
+ int length = input.getInt();
+ input.position(input.position() + length);
+ }
+ return readValue(input, Server.VERSION_3);
+ }
+ catch (BufferUnderflowException e)
+ {
+ throw new MarshalException("Not enough bytes to read a list");
+ }
+ }
+
public String toString(List<T> value)
{
StringBuilder sb = new StringBuilder();
@@ -118,13 +146,9 @@ public class ListSerializer<T> extends CollectionSerializer<List<T>>
for (T element : value)
{
if (isFirst)
- {
isFirst = false;
- }
else
- {
sb.append("; ");
- }
sb.append(elements.toString(element));
}
return sb.toString();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/serializers/MapSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/MapSerializer.java b/src/java/org/apache/cassandra/serializers/MapSerializer.java
index dadadd0..bc8e509 100644
--- a/src/java/org/apache/cassandra/serializers/MapSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/MapSerializer.java
@@ -22,6 +22,8 @@ import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.*;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.transport.Server;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.Pair;
@@ -114,6 +116,38 @@ public class MapSerializer<K, V> extends CollectionSerializer<Map<K, V>>
}
}
+ /**
+ * Given a serialized map, gets the value associated with a given key.
+ * @param serializedMap a serialized map
+ * @param serializedKey a serialized key
+ * @param keyType the key type for the map
+ * @return the value associated with the key if one exists, null otherwise
+ */
+ public ByteBuffer getSerializedValue(ByteBuffer serializedMap, ByteBuffer serializedKey, AbstractType keyType)
+ {
+ try
+ {
+ ByteBuffer input = serializedMap.duplicate();
+ int n = readCollectionSize(input, Server.VERSION_3);
+ for (int i = 0; i < n; i++)
+ {
+ ByteBuffer kbb = readValue(input, Server.VERSION_3);
+ ByteBuffer vbb = readValue(input, Server.VERSION_3);
+ int comparison = keyType.compare(kbb, serializedKey);
+ if (comparison == 0)
+ return vbb;
+ else if (comparison > 0)
+ // since the map is in sorted order, we know we've gone too far and the element doesn't exist
+ return null;
+ }
+ return null;
+ }
+ catch (BufferUnderflowException e)
+ {
+ throw new MarshalException("Not enough bytes to read a map");
+ }
+ }
+
public String toString(Map<K, V> value)
{
StringBuilder sb = new StringBuilder();
@@ -121,13 +155,9 @@ public class MapSerializer<K, V> extends CollectionSerializer<Map<K, V>>
for (Map.Entry<K, V> element : value.entrySet())
{
if (isFirst)
- {
isFirst = false;
- }
else
- {
sb.append("; ");
- }
sb.append('(');
sb.append(keys.toString(element.getKey()));
sb.append(", ");
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/transport/DataType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/transport/DataType.java b/src/java/org/apache/cassandra/transport/DataType.java
index a45d7ce..0ea353a 100644
--- a/src/java/org/apache/cassandra/transport/DataType.java
+++ b/src/java/org/apache/cassandra/transport/DataType.java
@@ -214,18 +214,18 @@ public enum DataType implements OptionCodec.Codecable<DataType>
{
if (type instanceof ListType)
{
- return Pair.<DataType, Object>create(LIST, ((ListType)type).elements);
+ return Pair.<DataType, Object>create(LIST, ((ListType)type).getElementsType());
}
else if (type instanceof MapType)
{
MapType mt = (MapType)type;
- return Pair.<DataType, Object>create(MAP, Arrays.asList(mt.keys, mt.values));
+ return Pair.<DataType, Object>create(MAP, Arrays.asList(mt.getKeysType(), mt.getValuesType()));
}
- else
+ else if (type instanceof SetType)
{
- assert type instanceof SetType;
- return Pair.<DataType, Object>create(SET, ((SetType)type).elements);
+ return Pair.<DataType, Object>create(SET, ((SetType)type).getElementsType());
}
+ throw new AssertionError();
}
if (type instanceof UserType && version >= 3)
@@ -251,12 +251,12 @@ public enum DataType implements OptionCodec.Codecable<DataType>
case CUSTOM:
return TypeParser.parse((String)entry.right);
case LIST:
- return ListType.getInstance((AbstractType)entry.right);
+ return ListType.getInstance((AbstractType)entry.right, true);
case SET:
- return SetType.getInstance((AbstractType)entry.right);
+ return SetType.getInstance((AbstractType)entry.right, true);
case MAP:
List<AbstractType> l = (List<AbstractType>)entry.right;
- return MapType.getInstance(l.get(0), l.get(1));
+ return MapType.getInstance(l.get(0), l.get(1), true);
case UDT:
return (AbstractType)entry.right;
case TUPLE:
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index 6e4a5a9..aacfb4b 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -37,7 +37,6 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.Schema;
-import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.Directories;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.marshal.*;
@@ -45,7 +44,6 @@ import org.apache.cassandra.exceptions.*;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.service.StorageService;
-import org.apache.cassandra.utils.ByteBufferUtil;
/**
* Base class for CQL tests.
@@ -96,10 +94,10 @@ public abstract class CQLTester
{
try
{
- schemaChange(String.format("DROP TABLE %s.%s", KEYSPACE, tableToDrop));
+ schemaChange(String.format("DROP TABLE IF EXISTS %s.%s", KEYSPACE, tableToDrop));
for (String typeName : typesToDrop)
- schemaChange(String.format("DROP TYPE %s.%s", KEYSPACE, typeName));
+ schemaChange(String.format("DROP TYPE IF EXISTS %s.%s", KEYSPACE, typeName));
// Dropping doesn't delete the sstables. It's not a huge deal but it's cleaner to cleanup after us
// Thas said, we shouldn't delete blindly before the SSTableDeletingTask for the table we drop
@@ -186,6 +184,21 @@ public abstract class CQLTester
schemaChange(fullQuery);
}
+ protected void createTableMayThrow(String query) throws Throwable
+ {
+ currentTable = "table_" + seqNumber.getAndIncrement();
+ String fullQuery = String.format(query, KEYSPACE + "." + currentTable);
+ logger.info(fullQuery);
+ try
+ {
+ QueryProcessor.executeOnceInternal(fullQuery);
+ }
+ catch (RuntimeException ex)
+ {
+ throw ex.getCause();
+ }
+ }
+
protected void alterTable(String query)
{
String fullQuery = String.format(query, KEYSPACE + "." + currentTable);
@@ -193,6 +206,20 @@ public abstract class CQLTester
schemaChange(fullQuery);
}
+ protected void alterTableMayThrow(String query) throws Throwable
+ {
+ String fullQuery = String.format(query, KEYSPACE + "." + currentTable);
+ logger.info(fullQuery);
+ try
+ {
+ QueryProcessor.executeOnceInternal(fullQuery);
+ }
+ catch (RuntimeException ex)
+ {
+ throw ex.getCause();
+ }
+ }
+
protected void createIndex(String query)
{
String fullQuery = String.format(query, KEYSPACE + "." + currentTable);
@@ -200,6 +227,20 @@ public abstract class CQLTester
schemaChange(fullQuery);
}
+ protected void createIndexMayThrow(String query) throws Throwable
+ {
+ String fullQuery = String.format(query, KEYSPACE + "." + currentTable);
+ logger.info(fullQuery);
+ try
+ {
+ QueryProcessor.executeOnceInternal(fullQuery);
+ }
+ catch (RuntimeException ex)
+ {
+ throw ex.getCause();
+ }
+ }
+
private static void schemaChange(String query)
{
try
@@ -262,7 +303,7 @@ public abstract class CQLTester
int i = 0;
while (iter.hasNext() && i < rows.length)
{
- Object[] expected = rows[i++];
+ Object[] expected = rows[i];
UntypedResultSet.Row actual = iter.next();
Assert.assertEquals(String.format("Invalid number of (expected) values provided for row %d", i), meta.size(), expected.length);
@@ -278,6 +319,7 @@ public abstract class CQLTester
Assert.fail(String.format("Invalid value for row %d column %d (%s of type %s), expected <%s> but got <%s>",
i, j, column.name, column.type.asCQL3Type(), formatValue(expectedByteValue, column.type), formatValue(actualValue, column.type)));
}
+ i++;
}
if (iter.hasNext())
@@ -311,6 +353,11 @@ public abstract class CQLTester
protected void assertInvalid(String query, Object... values) throws Throwable
{
+ assertInvalidMessage(null, query, values);
+ }
+
+ protected void assertInvalidMessage(String errorMessage, String query, Object... values) throws Throwable
+ {
try
{
execute(query, values);
@@ -321,7 +368,11 @@ public abstract class CQLTester
}
catch (InvalidRequestException e)
{
- // This is what we expect
+ if (errorMessage != null)
+ {
+ Assert.assertTrue("Expected error message to contain '" + errorMessage + "', but got '" + e.getMessage() + "'",
+ e.getMessage().contains(errorMessage));
+ }
}
}
@@ -401,7 +452,15 @@ public abstract class CQLTester
continue;
}
- buffers[i] = typeFor(value).decompose(serializeTuples(value));
+ try
+ {
+ buffers[i] = typeFor(value).decompose(serializeTuples(value));
+ }
+ catch (Exception ex)
+ {
+ logger.info("Error serializing query parameter {}:", value, ex);
+ throw ex;
+ }
}
return buffers;
}
@@ -613,14 +672,14 @@ public abstract class CQLTester
{
List l = (List)value;
AbstractType elt = l.isEmpty() ? BytesType.instance : typeFor(l.get(0));
- return ListType.getInstance(elt);
+ return ListType.getInstance(elt, true);
}
if (value instanceof Set)
{
Set s = (Set)value;
AbstractType elt = s.isEmpty() ? BytesType.instance : typeFor(s.iterator().next());
- return SetType.getInstance(elt);
+ return SetType.getInstance(elt, true);
}
if (value instanceof Map)
@@ -638,7 +697,7 @@ public abstract class CQLTester
keys = typeFor(entry.getKey());
values = typeFor(entry.getValue());
}
- return MapType.getInstance(keys, values);
+ return MapType.getInstance(keys, values, true);
}
throw new IllegalArgumentException("Unsupported value type (value is " + value + ")");
@@ -674,5 +733,10 @@ public abstract class CQLTester
sb.append(")");
return sb.toString();
}
+
+ public String toString()
+ {
+ return "TupleValue" + toCQLString();
+ }
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/test/unit/org/apache/cassandra/cql3/ColumnConditionTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/ColumnConditionTest.java b/test/unit/org/apache/cassandra/cql3/ColumnConditionTest.java
index 205babc..3b1a826 100644
--- a/test/unit/org/apache/cassandra/cql3/ColumnConditionTest.java
+++ b/test/unit/org/apache/cassandra/cql3/ColumnConditionTest.java
@@ -154,9 +154,9 @@ public class ColumnConditionTest
{
CFMetaData cfm = CFMetaData.compile("create table foo(a int PRIMARY KEY, b int, c list<int>)", "ks");
Map<ByteBuffer, CollectionType> typeMap = new HashMap<>();
- typeMap.put(ByteBufferUtil.bytes("c"), ListType.getInstance(Int32Type.instance));
+ typeMap.put(ByteBufferUtil.bytes("c"), ListType.getInstance(Int32Type.instance, true));
CompoundSparseCellNameType.WithCollection nameType = new CompoundSparseCellNameType.WithCollection(Collections.EMPTY_LIST, ColumnToCollectionType.getInstance(typeMap));
- ColumnDefinition definition = new ColumnDefinition(cfm, ByteBufferUtil.bytes("c"), ListType.getInstance(Int32Type.instance), 0, ColumnDefinition.Kind.REGULAR);
+ ColumnDefinition definition = new ColumnDefinition(cfm, ByteBufferUtil.bytes("c"), ListType.getInstance(Int32Type.instance, true), 0, ColumnDefinition.Kind.REGULAR);
List<Cell> cells = new ArrayList<>(columnValues.size());
if (columnValues != null)
@@ -169,14 +169,14 @@ public class ColumnConditionTest
};
}
- return bound.listAppliesTo(ListType.getInstance(Int32Type.instance), cells == null ? null : cells.iterator(), conditionValues, bound.operator);
+ return bound.listAppliesTo(ListType.getInstance(Int32Type.instance, true), cells == null ? null : cells.iterator(), conditionValues, bound.operator);
}
@Test
// sets use the same check as lists
public void testListCollectionBoundAppliesTo() throws InvalidRequestException
{
- ColumnDefinition definition = new ColumnDefinition("ks", "cf", new ColumnIdentifier("c", true), ListType.getInstance(Int32Type.instance), null, null, null, null, null);
+ ColumnDefinition definition = new ColumnDefinition("ks", "cf", new ColumnIdentifier("c", true), ListType.getInstance(Int32Type.instance, true), null, null, null, null, null);
// EQ
ColumnCondition condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.EQ);
@@ -275,9 +275,9 @@ public class ColumnConditionTest
assertTrue(listAppliesTo(bound, list(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
}
- private static Set<ByteBuffer> set(ByteBuffer... values)
+ private static SortedSet<ByteBuffer> set(ByteBuffer... values)
{
- Set results = new HashSet<ByteBuffer>(values.length);
+ SortedSet<ByteBuffer> results = new TreeSet<>(Int32Type.instance);
results.addAll(Arrays.asList(values));
return results;
}
@@ -286,9 +286,9 @@ public class ColumnConditionTest
{
CFMetaData cfm = CFMetaData.compile("create table foo(a int PRIMARY KEY, b int, c set<int>)", "ks");
Map<ByteBuffer, CollectionType> typeMap = new HashMap<>();
- typeMap.put(ByteBufferUtil.bytes("c"), SetType.getInstance(Int32Type.instance));
+ typeMap.put(ByteBufferUtil.bytes("c"), SetType.getInstance(Int32Type.instance, true));
CompoundSparseCellNameType.WithCollection nameType = new CompoundSparseCellNameType.WithCollection(Collections.EMPTY_LIST, ColumnToCollectionType.getInstance(typeMap));
- ColumnDefinition definition = new ColumnDefinition(cfm, ByteBufferUtil.bytes("c"), SetType.getInstance(Int32Type.instance), 0, ColumnDefinition.Kind.REGULAR);
+ ColumnDefinition definition = new ColumnDefinition(cfm, ByteBufferUtil.bytes("c"), SetType.getInstance(Int32Type.instance, true), 0, ColumnDefinition.Kind.REGULAR);
List<Cell> cells = new ArrayList<>(columnValues.size());
if (columnValues != null)
@@ -300,13 +300,13 @@ public class ColumnConditionTest
};
}
- return bound.setAppliesTo(SetType.getInstance(Int32Type.instance), cells == null ? null : cells.iterator(), conditionValues, bound.operator);
+ return bound.setAppliesTo(SetType.getInstance(Int32Type.instance, true), cells == null ? null : cells.iterator(), conditionValues, bound.operator);
}
@Test
public void testSetCollectionBoundAppliesTo() throws InvalidRequestException
{
- ColumnDefinition definition = new ColumnDefinition("ks", "cf", new ColumnIdentifier("c", true), SetType.getInstance(Int32Type.instance), null, null, null, null, null);
+ ColumnDefinition definition = new ColumnDefinition("ks", "cf", new ColumnIdentifier("c", true), SetType.getInstance(Int32Type.instance, true), null, null, null, null, null);
// EQ
ColumnCondition condition = ColumnCondition.condition(definition, null, new Sets.Value(set(ONE)), Operator.EQ);
@@ -419,9 +419,9 @@ public class ColumnConditionTest
{
CFMetaData cfm = CFMetaData.compile("create table foo(a int PRIMARY KEY, b map<int, int>)", "ks");
Map<ByteBuffer, CollectionType> typeMap = new HashMap<>();
- typeMap.put(ByteBufferUtil.bytes("b"), MapType.getInstance(Int32Type.instance, Int32Type.instance));
+ typeMap.put(ByteBufferUtil.bytes("b"), MapType.getInstance(Int32Type.instance, Int32Type.instance, true));
CompoundSparseCellNameType.WithCollection nameType = new CompoundSparseCellNameType.WithCollection(Collections.EMPTY_LIST, ColumnToCollectionType.getInstance(typeMap));
- ColumnDefinition definition = new ColumnDefinition(cfm, ByteBufferUtil.bytes("b"), MapType.getInstance(Int32Type.instance, Int32Type.instance), 0, ColumnDefinition.Kind.REGULAR);
+ ColumnDefinition definition = new ColumnDefinition(cfm, ByteBufferUtil.bytes("b"), MapType.getInstance(Int32Type.instance, Int32Type.instance, true), 0, ColumnDefinition.Kind.REGULAR);
List<Cell> cells = new ArrayList<>(columnValues.size());
if (columnValues != null)
@@ -430,13 +430,13 @@ public class ColumnConditionTest
cells.add(new BufferCell(nameType.create(Composites.EMPTY, definition, entry.getKey()), entry.getValue()));
}
- return bound.mapAppliesTo(MapType.getInstance(Int32Type.instance, Int32Type.instance), cells.iterator(), conditionValues, bound.operator);
+ return bound.mapAppliesTo(MapType.getInstance(Int32Type.instance, Int32Type.instance, true), cells.iterator(), conditionValues, bound.operator);
}
@Test
public void testMapCollectionBoundIsSatisfiedByValue() throws InvalidRequestException
{
- ColumnDefinition definition = new ColumnDefinition("ks", "cf", new ColumnIdentifier("b", true), MapType.getInstance(Int32Type.instance, Int32Type.instance), null, null, null, null, null);
+ ColumnDefinition definition = new ColumnDefinition("ks", "cf", new ColumnIdentifier("b", true), MapType.getInstance(Int32Type.instance, Int32Type.instance, true), null, null, null, null, null);
Map<ByteBuffer, ByteBuffer> placeholderMap = new TreeMap<>();
placeholderMap.put(ONE, ONE);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/test/unit/org/apache/cassandra/cql3/FrozenCollectionsTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/FrozenCollectionsTest.java b/test/unit/org/apache/cassandra/cql3/FrozenCollectionsTest.java
new file mode 100644
index 0000000..203adae
--- /dev/null
+++ b/test/unit/org/apache/cassandra/cql3/FrozenCollectionsTest.java
@@ -0,0 +1,791 @@
+/*
+ * 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.cql3;
+
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.SyntaxException;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class FrozenCollectionsTest extends CQLTester
+{
+ @Test
+ public void testPartitionKeyUsage() throws Throwable
+ {
+ createTable("CREATE TABLE %s (k frozen<set<int>> PRIMARY KEY, v int)");
+
+ execute("INSERT INTO %s (k, v) VALUES (?, ?)", set(), 1);
+ execute("INSERT INTO %s (k, v) VALUES (?, ?)", set(1, 2, 3), 1);
+ execute("INSERT INTO %s (k, v) VALUES (?, ?)", set(4, 5, 6), 0);
+ execute("INSERT INTO %s (k, v) VALUES (?, ?)", set(7, 8, 9), 0);
+
+ // overwrite with an update
+ execute("UPDATE %s SET v=? WHERE k=?", 0, set());
+ execute("UPDATE %s SET v=? WHERE k=?", 0, set(1, 2, 3));
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(set(), 0),
+ row(set(1, 2, 3), 0),
+ row(set(4, 5, 6), 0),
+ row(set(7, 8, 9), 0)
+ );
+
+ assertRows(execute("SELECT k FROM %s"),
+ row(set()),
+ row(set(1, 2, 3)),
+ row(set(4, 5, 6)),
+ row(set(7, 8, 9))
+ );
+
+ assertRows(execute("SELECT * FROM %s LIMIT 2"),
+ row(set(), 0),
+ row(set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE k=?", set(4, 5, 6)),
+ row(set(4, 5, 6), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE k=?", set()),
+ row(set(), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE k IN ?", list(set(4, 5, 6), set())),
+ row(set(4, 5, 6), 0),
+ row(set(), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE token(k) >= token(?)", set(4, 5, 6)),
+ row(set(4, 5, 6), 0),
+ row(set(7, 8, 9), 0)
+ );
+
+ assertInvalid("INSERT INTO %s (k, v) VALUES (null, 0)");
+
+ execute("DELETE FROM %s WHERE k=?", set());
+ execute("DELETE FROM %s WHERE k=?", set(4, 5, 6));
+ assertRows(execute("SELECT * FROM %s"),
+ row(set(1, 2, 3), 0),
+ row(set(7, 8, 9), 0)
+ );
+ }
+
+ @Test
+ public void testNestedPartitionKeyUsage() throws Throwable
+ {
+ createTable("CREATE TABLE %s (k frozen<map<set<int>, list<int>>> PRIMARY KEY, v int)");
+
+ execute("INSERT INTO %s (k, v) VALUES (?, ?)", map(), 1);
+ execute("INSERT INTO %s (k, v) VALUES (?, ?)", map(set(), list(1, 2, 3)), 0);
+ execute("INSERT INTO %s (k, v) VALUES (?, ?)", map(set(1, 2, 3), list(1, 2, 3)), 1);
+ execute("INSERT INTO %s (k, v) VALUES (?, ?)", map(set(4, 5, 6), list(1, 2, 3)), 0);
+ execute("INSERT INTO %s (k, v) VALUES (?, ?)", map(set(7, 8, 9), list(1, 2, 3)), 0);
+
+ // overwrite with an update
+ execute("UPDATE %s SET v=? WHERE k=?", 0, map());
+ execute("UPDATE %s SET v=? WHERE k=?", 0, map(set(1, 2, 3), list(1, 2, 3)));
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(map(), 0),
+ row(map(set(), list(1, 2, 3)), 0),
+ row(map(set(1, 2, 3), list(1, 2, 3)), 0),
+ row(map(set(4, 5, 6), list(1, 2, 3)), 0),
+ row(map(set(7, 8, 9), list(1, 2, 3)), 0)
+ );
+
+ assertRows(execute("SELECT k FROM %s"),
+ row(map()),
+ row(map(set(), list(1, 2, 3))),
+ row(map(set(1, 2, 3), list(1, 2, 3))),
+ row(map(set(4, 5, 6), list(1, 2, 3))),
+ row(map(set(7, 8, 9), list(1, 2, 3)))
+ );
+
+ assertRows(execute("SELECT * FROM %s LIMIT 3"),
+ row(map(), 0),
+ row(map(set(), list(1, 2, 3)), 0),
+ row(map(set(1, 2, 3), list(1, 2, 3)), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE k=?", map(set(4, 5, 6), list(1, 2, 3))),
+ row(map(set(4, 5, 6), list(1, 2, 3)), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE k=?", map()),
+ row(map(), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE k=?", map(set(), list(1, 2, 3))),
+ row(map(set(), list(1, 2, 3)), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE k IN ?", list(map(set(4, 5, 6), list(1, 2, 3)), map(), map(set(), list(1, 2, 3)))),
+ row(map(set(4, 5, 6), list(1, 2, 3)), 0),
+ row(map(), 0),
+ row(map(set(), list(1, 2, 3)), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE token(k) >= token(?)", map(set(4, 5, 6), list(1, 2, 3))),
+ row(map(set(4, 5, 6), list(1, 2, 3)), 0),
+ row(map(set(7, 8, 9), list(1, 2, 3)), 0)
+ );
+
+ execute("DELETE FROM %s WHERE k=?", map());
+ execute("DELETE FROM %s WHERE k=?", map(set(), list(1, 2, 3)));
+ execute("DELETE FROM %s WHERE k=?", map(set(4, 5, 6), list(1, 2, 3)));
+ assertRows(execute("SELECT * FROM %s"),
+ row(map(set(1, 2, 3), list(1, 2, 3)), 0),
+ row(map(set(7, 8, 9), list(1, 2, 3)), 0)
+ );
+
+ }
+
+ @Test
+ public void testClusteringKeyUsage() throws Throwable
+ {
+ for (String option : Arrays.asList("", " WITH COMPACT STORAGE"))
+ {
+ createTable("CREATE TABLE %s (a int, b frozen<set<int>>, c int, PRIMARY KEY (a, b))" + option);
+
+ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, set(), 1);
+ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, set(1, 2, 3), 1);
+ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, set(4, 5, 6), 0);
+ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, set(7, 8, 9), 0);
+
+ // overwrite with an update
+ execute("UPDATE %s SET c=? WHERE a=? AND b=?", 0, 0, set());
+ execute("UPDATE %s SET c=? WHERE a=? AND b=?", 0, 0, set(1, 2, 3));
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(0, set(), 0),
+ row(0, set(1, 2, 3), 0),
+ row(0, set(4, 5, 6), 0),
+ row(0, set(7, 8, 9), 0)
+ );
+
+ assertRows(execute("SELECT b FROM %s"),
+ row(set()),
+ row(set(1, 2, 3)),
+ row(set(4, 5, 6)),
+ row(set(7, 8, 9))
+ );
+
+ assertRows(execute("SELECT * FROM %s LIMIT 2"),
+ row(0, set(), 0),
+ row(0, set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, set(4, 5, 6)),
+ row(0, set(4, 5, 6), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, set()),
+ row(0, set(), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b IN ?", 0, list(set(4, 5, 6), set())),
+ row(0, set(), 0),
+ row(0, set(4, 5, 6), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ?", 0, set(4, 5, 6)),
+ row(0, set(7, 8, 9), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b >= ?", 0, set(4, 5, 6)),
+ row(0, set(4, 5, 6), 0),
+ row(0, set(7, 8, 9), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b < ?", 0, set(4, 5, 6)),
+ row(0, set(), 0),
+ row(0, set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b <= ?", 0, set(4, 5, 6)),
+ row(0, set(), 0),
+ row(0, set(1, 2, 3), 0),
+ row(0, set(4, 5, 6), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ? AND b <= ?", 0, set(1, 2, 3), set(4, 5, 6)),
+ row(0, set(4, 5, 6), 0)
+ );
+
+ execute("DELETE FROM %s WHERE a=? AND b=?", 0, set());
+ execute("DELETE FROM %s WHERE a=? AND b=?", 0, set(4, 5, 6));
+ assertRows(execute("SELECT * FROM %s"),
+ row(0, set(1, 2, 3), 0),
+ row(0, set(7, 8, 9), 0)
+ );
+ }
+ }
+
+ @Test
+ public void testNestedClusteringKeyUsage() throws Throwable
+ {
+ for (String option : Arrays.asList("", " WITH COMPACT STORAGE"))
+ {
+ createTable("CREATE TABLE %s (a int, b frozen<map<set<int>, list<int>>>, c frozen<set<int>>, d int, PRIMARY KEY (a, b, c))" + option);
+
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(), set(), 0);
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(set(), list(1, 2, 3)), set(), 0);
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0);
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0);
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0);
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(0, map(), set(), 0),
+ row(0, map(set(), list(1, 2, 3)), set(), 0),
+ row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0),
+ row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0),
+ row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT b FROM %s"),
+ row(map()),
+ row(map(set(), list(1, 2, 3))),
+ row(map(set(1, 2, 3), list(1, 2, 3))),
+ row(map(set(4, 5, 6), list(1, 2, 3))),
+ row(map(set(7, 8, 9), list(1, 2, 3)))
+ );
+
+ assertRows(execute("SELECT c FROM %s"),
+ row(set()),
+ row(set()),
+ row(set(1, 2, 3)),
+ row(set(1, 2, 3)),
+ row(set(1, 2, 3))
+ );
+
+ assertRows(execute("SELECT * FROM %s LIMIT 3"),
+ row(0, map(), set(), 0),
+ row(0, map(set(), list(1, 2, 3)), set(), 0),
+ row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=0 ORDER BY b DESC LIMIT 4"),
+ row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0),
+ row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0),
+ row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0),
+ row(0, map(set(), list(1, 2, 3)), set(), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, map()),
+ row(0, map(), set(), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, map(set(), list(1, 2, 3))),
+ row(0, map(set(), list(1, 2, 3)), set(), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, map(set(1, 2, 3), list(1, 2, 3))),
+ row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(), list(1, 2, 3)), set()),
+ row(0, map(set(), list(1, 2, 3)), set(), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND (b, c) IN ?", 0, list(tuple(map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3)),
+ tuple(map(), set()))),
+ row(0, map(), set(), 0),
+ row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ?", 0, map(set(4, 5, 6), list(1, 2, 3))),
+ row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b >= ?", 0, map(set(4, 5, 6), list(1, 2, 3))),
+ row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0),
+ row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b < ?", 0, map(set(4, 5, 6), list(1, 2, 3))),
+ row(0, map(), set(), 0),
+ row(0, map(set(), list(1, 2, 3)), set(), 0),
+ row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b <= ?", 0, map(set(4, 5, 6), list(1, 2, 3))),
+ row(0, map(), set(), 0),
+ row(0, map(set(), list(1, 2, 3)), set(), 0),
+ row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0),
+ row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ? AND b <= ?", 0, map(set(1, 2, 3), list(1, 2, 3)), map(set(4, 5, 6), list(1, 2, 3))),
+ row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+
+ execute("DELETE FROM %s WHERE a=? AND b=? AND c=?", 0, map(), set());
+ assertEmpty(execute("SELECT * FROM %s WHERE a=? AND b=? AND c=?", 0, map(), set()));
+
+ execute("DELETE FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(), list(1, 2, 3)), set());
+ assertEmpty(execute("SELECT * FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(), list(1, 2, 3)), set()));
+
+ execute("DELETE FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3));
+ assertEmpty(execute("SELECT * FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3)));
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0),
+ row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0)
+ );
+ }
+ }
+
+ @Test
+ public void testNormalColumnUsage() throws Throwable
+ {
+ for (String option : Arrays.asList("", " WITH COMPACT STORAGE"))
+ {
+ createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<map<set<int>, list<int>>>, c frozen<set<int>>)" + option);
+
+ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, map(), set());
+ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 1, map(set(), list(99999, 999999, 99999)), set());
+ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 2, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3));
+ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 3, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3));
+ execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 4, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3));
+
+ // overwrite with update
+ execute ("UPDATE %s SET b=? WHERE a=?", map(set(), list(1, 2, 3)), 1);
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(0, map(), set()),
+ row(1, map(set(), list(1, 2, 3)), set()),
+ row(2, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3)),
+ row(3, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3)),
+ row(4, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3))
+ );
+
+ assertRows(execute("SELECT b FROM %s"),
+ row(map()),
+ row(map(set(), list(1, 2, 3))),
+ row(map(set(1, 2, 3), list(1, 2, 3))),
+ row(map(set(4, 5, 6), list(1, 2, 3))),
+ row(map(set(7, 8, 9), list(1, 2, 3)))
+ );
+
+ assertRows(execute("SELECT c FROM %s"),
+ row(set()),
+ row(set()),
+ row(set(1, 2, 3)),
+ row(set(1, 2, 3)),
+ row(set(1, 2, 3))
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=?", 3),
+ row(3, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3))
+ );
+
+ execute("UPDATE %s SET b=? WHERE a=?", null, 1);
+ assertRows(execute("SELECT * FROM %s WHERE a=?", 1),
+ row(1, null, set())
+ );
+
+ execute("UPDATE %s SET b=? WHERE a=?", map(), 1);
+ assertRows(execute("SELECT * FROM %s WHERE a=?", 1),
+ row(1, map(), set())
+ );
+
+ execute("UPDATE %s SET c=? WHERE a=?", null, 2);
+ assertRows(execute("SELECT * FROM %s WHERE a=?", 2),
+ row(2, map(set(1, 2, 3), list(1, 2, 3)), null)
+ );
+
+ execute("UPDATE %s SET c=? WHERE a=?", set(), 2);
+ assertRows(execute("SELECT * FROM %s WHERE a=?", 2),
+ row(2, map(set(1, 2, 3), list(1, 2, 3)), set())
+ );
+
+ execute("DELETE b FROM %s WHERE a=?", 3);
+ assertRows(execute("SELECT * FROM %s WHERE a=?", 3),
+ row(3, null, set(1, 2, 3))
+ );
+
+ execute("DELETE c FROM %s WHERE a=?", 4);
+ assertRows(execute("SELECT * FROM %s WHERE a=?", 4),
+ row(4, map(set(7, 8, 9), list(1, 2, 3)), null)
+ );
+ }
+ }
+
+ @Test
+ public void testStaticColumnUsage() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b int, c frozen<map<set<int>, list<int>>> static, d int, PRIMARY KEY (a, b))");
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 0, map(), 0);
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 1, map(), 0);
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, 0, map(set(), list(1, 2, 3)), 0);
+ execute("INSERT INTO %s (a, b, d) VALUES (?, ?, ?)", 1, 1, 0);
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 2, 0, map(set(1, 2, 3), list(1, 2, 3)), 0);
+
+ assertRows(execute("SELECT * FROM %s"),
+ row(0, 0, map(), 0),
+ row(0, 1, map(), 0),
+ row(1, 0, map(set(), list(1, 2, 3)), 0),
+ row(1, 1, map(set(), list(1, 2, 3)), 0),
+ row(2, 0, map(set(1, 2, 3), list(1, 2, 3)), 0)
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, 1),
+ row(0, 1, map(), 0)
+ );
+
+ execute("DELETE c FROM %s WHERE a=?", 0);
+ assertRows(execute("SELECT * FROM %s"),
+ row(0, 0, null, 0),
+ row(0, 1, null, 0),
+ row(1, 0, map(set(), list(1, 2, 3)), 0),
+ row(1, 1, map(set(), list(1, 2, 3)), 0),
+ row(2, 0, map(set(1, 2, 3), list(1, 2, 3)), 0)
+ );
+
+ execute("DELETE FROM %s WHERE a=?", 0);
+ assertRows(execute("SELECT * FROM %s"),
+ row(1, 0, map(set(), list(1, 2, 3)), 0),
+ row(1, 1, map(set(), list(1, 2, 3)), 0),
+ row(2, 0, map(set(1, 2, 3), list(1, 2, 3)), 0)
+ );
+
+ execute("UPDATE %s SET c=? WHERE a=?", map(set(1, 2, 3), list(1, 2, 3)), 1);
+ assertRows(execute("SELECT * FROM %s"),
+ row(1, 0, map(set(1, 2, 3), list(1, 2, 3)), 0),
+ row(1, 1, map(set(1, 2, 3), list(1, 2, 3)), 0),
+ row(2, 0, map(set(1, 2, 3), list(1, 2, 3)), 0)
+ );
+ }
+
+ private void assertInvalidCreateWithMessage(String createTableStatement, String errorMessage) throws Throwable
+ {
+ try
+ {
+ createTableMayThrow(createTableStatement);
+ Assert.fail("Expected CREATE TABLE statement to error: " + createTableStatement);
+ }
+ catch (InvalidRequestException | ConfigurationException | SyntaxException ex)
+ {
+ Assert.assertTrue("Expected error message to contain '" + errorMessage + "', but got '" + ex.getMessage() + "'",
+ ex.getMessage().contains(errorMessage));
+ }
+ }
+
+ private void assertInvalidAlterWithMessage(String createTableStatement, String errorMessage) throws Throwable
+ {
+ try
+ {
+ alterTableMayThrow(createTableStatement);
+ Assert.fail("Expected CREATE TABLE statement to error: " + createTableStatement);
+ }
+ catch (InvalidRequestException | ConfigurationException ex)
+ {
+ Assert.assertTrue("Expected error message to contain '" + errorMessage + "', but got '" + ex.getMessage() + "'",
+ ex.getMessage().contains(errorMessage));
+ }
+ }
+
+ @Test
+ public void testInvalidOperations() throws Throwable
+ {
+ // lists
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, l frozen<list<int>>)");
+ assertInvalid("UPDATE %s SET l[?]=? WHERE k=?", 0, 0, 0);
+ assertInvalid("UPDATE %s SET l = ? + l WHERE k=?", list(0), 0);
+ assertInvalid("UPDATE %s SET l = l + ? WHERE k=?", list(4), 0);
+ assertInvalid("UPDATE %s SET l = l - ? WHERE k=?", list(3), 0);
+ assertInvalid("DELETE l[?] FROM %s WHERE k=?", 0, 0);
+
+ // sets
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<int>>)");
+ assertInvalid("UPDATE %s SET s = s + ? WHERE k=?", set(0), 0);
+ assertInvalid("UPDATE %s SET s = s - ? WHERE k=?", set(3), 0);
+
+ // maps
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int, int>>)");
+ assertInvalid("UPDATE %s SET m[?]=? WHERE k=?", 0, 0, 0);
+ assertInvalid("UPDATE %s SET m = m + ? WHERE k=?", map(4, 4), 0);
+ assertInvalid("DELETE m[?] FROM %s WHERE k=?", 0, 0);
+
+ assertInvalidCreateWithMessage("CREATE TABLE %s (k int PRIMARY KEY, t set<set<int>>)",
+ "Non-frozen collections are not allowed inside collections");
+
+ assertInvalidCreateWithMessage("CREATE TABLE %s (k int PRIMARY KEY, t frozen<set<counter>>)",
+ "Counters are not allowed inside collections");
+
+ assertInvalidCreateWithMessage("CREATE TABLE %s (k int PRIMARY KEY, t frozen<text>)",
+ "frozen<> is only allowed on collections, tuples, and user-defined types");
+ }
+
+ @Test
+ public void testAltering() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int, b frozen<list<int>>, c frozen<list<int>>, PRIMARY KEY (a, b))");
+
+ alterTable("ALTER TABLE %s ALTER c TYPE frozen<list<blob>>");
+
+ assertInvalidAlterWithMessage("ALTER TABLE %s ALTER b TYPE frozen<list<blob>>",
+ "types are not order-compatible");
+
+ assertInvalidAlterWithMessage("ALTER TABLE %s ALTER b TYPE list<int>",
+ "types are not order-compatible");
+
+ assertInvalidAlterWithMessage("ALTER TABLE %s ALTER c TYPE list<blob>",
+ "types are incompatible");
+
+ alterTable("ALTER TABLE %s DROP c");
+ alterTable("ALTER TABLE %s ADD c frozen<set<int>>");
+ assertInvalidAlterWithMessage("ALTER TABLE %s ALTER c TYPE frozen<set<blob>>",
+ "types are incompatible");
+
+ alterTable("ALTER TABLE %s DROP c");
+ alterTable("ALTER TABLE %s ADD c frozen<map<int, int>>");
+ assertInvalidAlterWithMessage("ALTER TABLE %s ALTER c TYPE frozen<map<blob, int>>",
+ "types are incompatible");
+ alterTable("ALTER TABLE %s ALTER c TYPE frozen<map<int, blob>>");
+ }
+
+ private void assertInvalidIndexCreationWithMessage(String statement, String errorMessage) throws Throwable
+ {
+ try
+ {
+ createIndexMayThrow(statement);
+ Assert.fail("Expected index creation to fail: " + statement);
+ }
+ catch (InvalidRequestException ex)
+ {
+ Assert.assertTrue("Expected error message to contain '" + errorMessage + "', but got '" + ex.getMessage() + "'",
+ ex.getMessage().contains(errorMessage));
+ }
+ }
+
+ @Test
+ public void testSecondaryIndex() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a frozen<map<int, text>> PRIMARY KEY, b frozen<map<int, text>>)");
+
+ // for now, we don't support indexing values or keys of collections in the primary key
+ assertInvalidIndexCreationWithMessage("CREATE INDEX ON %s (full(a))", "Cannot create secondary index on partition key column");
+ assertInvalidIndexCreationWithMessage("CREATE INDEX ON %s (keys(a))", "Cannot create index on keys of frozen<map> column");
+ assertInvalidIndexCreationWithMessage("CREATE INDEX ON %s (keys(b))", "Cannot create index on keys of frozen<map> column");
+
+ createTable("CREATE TABLE %s (a int, b frozen<list<int>>, c frozen<set<int>>, d frozen<map<int, text>>, PRIMARY KEY (a, b))");
+
+ createIndex("CREATE INDEX ON %s (full(b))");
+ createIndex("CREATE INDEX ON %s (full(c))");
+ createIndex("CREATE INDEX ON %s (full(d))");
+
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, list(1, 2, 3), set(1, 2, 3), map(1, "a"));
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, list(4, 5, 6), set(1, 2, 3), map(1, "a"));
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, list(1, 2, 3), set(4, 5, 6), map(2, "b"));
+ execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 1, list(4, 5, 6), set(4, 5, 6), map(2, "b"));
+
+ // CONTAINS KEY doesn't work on non-maps
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column",
+ "SELECT * FROM %s WHERE b CONTAINS KEY ?", 1);
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column",
+ "SELECT * FROM %s WHERE b CONTAINS KEY ? ALLOW FILTERING", 1);
+
+ assertInvalidMessage("Cannot use CONTAINS KEY on non-map column",
+ "SELECT * FROM %s WHERE c CONTAINS KEY ?", 1);
+
+ // normal indexes on frozen collections don't support CONTAINS or CONTAINS KEY
+ assertInvalidMessage("No secondary indexes on the restricted columns support the provided operator",
+ "SELECT * FROM %s WHERE b CONTAINS ?", 1);
+
+ assertInvalidMessage("No secondary indexes on the restricted columns support the provided operator",
+ "SELECT * FROM %s WHERE b CONTAINS ? ALLOW FILTERING", 1);
+
+ assertInvalidMessage("No secondary indexes on the restricted columns support the provided operator",
+ "SELECT * FROM %s WHERE d CONTAINS KEY ?", 1);
+
+ assertInvalidMessage("No secondary indexes on the restricted columns support the provided operator",
+ "SELECT * FROM %s WHERE d CONTAINS KEY ? ALLOW FILTERING", 1);
+
+ assertInvalidMessage("No secondary indexes on the restricted columns support the provided operator",
+ "SELECT * FROM %s WHERE b CONTAINS ? AND d CONTAINS KEY ? ALLOW FILTERING", 1, 1);
+
+ // index lookup on b
+ assertRows(execute("SELECT * FROM %s WHERE b=?", list(1, 2, 3)),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a")),
+ row(1, list(1, 2, 3), set(4, 5, 6), map(2, "b"))
+ );
+
+ assertEmpty(execute("SELECT * FROM %s WHERE b=?", list(-1)));
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE b=? AND c=?", list(1, 2, 3), set(4, 5, 6));
+ assertRows(execute("SELECT * FROM %s WHERE b=? AND c=? ALLOW FILTERING", list(1, 2, 3), set(4, 5, 6)),
+ row(1, list(1, 2, 3), set(4, 5, 6), map(2, "b"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE b=? AND c CONTAINS ?", list(1, 2, 3), 5);
+ assertRows(execute("SELECT * FROM %s WHERE b=? AND c CONTAINS ? ALLOW FILTERING", list(1, 2, 3), 5),
+ row(1, list(1, 2, 3), set(4, 5, 6), map(2, "b"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE b=? AND d=?", list(1, 2, 3), map(1, "a"));
+ assertRows(execute("SELECT * FROM %s WHERE b=? AND d=? ALLOW FILTERING", list(1, 2, 3), map(1, "a")),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE b=? AND d CONTAINS ?", list(1, 2, 3), "a");
+ assertRows(execute("SELECT * FROM %s WHERE b=? AND d CONTAINS ? ALLOW FILTERING", list(1, 2, 3), "a"),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE b=? AND d CONTAINS KEY ?", list(1, 2, 3), 1);
+ assertRows(execute("SELECT * FROM %s WHERE b=? AND d CONTAINS KEY ? ALLOW FILTERING", list(1, 2, 3), 1),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a"))
+ );
+
+ // index lookup on c
+ assertRows(execute("SELECT * FROM %s WHERE c=?", set(1, 2, 3)),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a")),
+ row(0, list(4, 5, 6), set(1, 2, 3), map(1, "a"))
+ );
+
+ // ordering of c should not matter
+ assertRows(execute("SELECT * FROM %s WHERE c=?", set(2, 1, 3)),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a")),
+ row(0, list(4, 5, 6), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertEmpty(execute("SELECT * FROM %s WHERE c=?", set(-1)));
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE c=? AND b=?", set(1, 2, 3), list(1, 2, 3));
+ assertRows(execute("SELECT * FROM %s WHERE c=? AND b=? ALLOW FILTERING", set(1, 2, 3), list(1, 2, 3)),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE c=? AND b CONTAINS ?", set(1, 2, 3), 1);
+ assertRows(execute("SELECT * FROM %s WHERE c=? AND b CONTAINS ? ALLOW FILTERING", set(1, 2, 3), 1),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE c=? AND d = ?", set(1, 2, 3), map(1, "a"));
+ assertRows(execute("SELECT * FROM %s WHERE c=? AND d = ? ALLOW FILTERING", set(1, 2, 3), map(1, "a")),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a")),
+ row(0, list(4, 5, 6), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE c=? AND d CONTAINS ?", set(1, 2, 3), "a");
+ assertRows(execute("SELECT * FROM %s WHERE c=? AND d CONTAINS ? ALLOW FILTERING", set(1, 2, 3), "a"),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a")),
+ row(0, list(4, 5, 6), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE c=? AND d CONTAINS KEY ?", set(1, 2, 3), 1);
+ assertRows(execute("SELECT * FROM %s WHERE c=? AND d CONTAINS KEY ? ALLOW FILTERING", set(1, 2, 3), 1),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a")),
+ row(0, list(4, 5, 6), set(1, 2, 3), map(1, "a"))
+ );
+
+ // index lookup on d
+ assertRows(execute("SELECT * FROM %s WHERE d=?", map(1, "a")),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a")),
+ row(0, list(4, 5, 6), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE d=?", map(2, "b")),
+ row(1, list(1, 2, 3), set(4, 5, 6), map(2, "b")),
+ row(1, list(4, 5, 6), set(4, 5, 6), map(2, "b"))
+ );
+
+ assertEmpty(execute("SELECT * FROM %s WHERE d=?", map(3, "c")));
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE d=? AND c=?", map(1, "a"), set(1, 2, 3));
+ assertRows(execute("SELECT * FROM %s WHERE d=? AND b=? ALLOW FILTERING", map(1, "a"), list(1, 2, 3)),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE d=? AND b CONTAINS ?", map(1, "a"), 3);
+ assertRows(execute("SELECT * FROM %s WHERE d=? AND b CONTAINS ? ALLOW FILTERING", map(1, "a"), 3),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertInvalidMessage("ALLOW FILTERING", "SELECT * FROM %s WHERE d=? AND b=? AND c=?", map(1, "a"), list(1, 2, 3), set(1, 2, 3));
+ assertRows(execute("SELECT * FROM %s WHERE d=? AND b=? AND c=? ALLOW FILTERING", map(1, "a"), list(1, 2, 3), set(1, 2, 3)),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a"))
+ );
+
+ assertRows(execute("SELECT * FROM %s WHERE d=? AND b CONTAINS ? AND c CONTAINS ? ALLOW FILTERING", map(1, "a"), 2, 2),
+ row(0, list(1, 2, 3), set(1, 2, 3), map(1, "a"))
+ );
+
+ execute("DELETE d FROM %s WHERE a=? AND b=?", 0, list(1, 2, 3));
+ assertRows(execute("SELECT * FROM %s WHERE d=?", map(1, "a")),
+ row(0, list(4, 5, 6), set(1, 2, 3), map(1, "a"))
+ );
+ }
+
+ @Test
+ public void testUserDefinedTypes() throws Throwable
+ {
+ String myType = createType("CREATE TYPE %s (a set<int>, b tuple<list<int>>)");
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, v frozen<" + myType + ">)");
+ execute("INSERT INTO %s (k, v) VALUES (?, {a: ?, b: ?})", 0, set(1, 2, 3), tuple(list(1, 2, 3)));
+ assertRows(execute("SELECT v.a, v.b FROM %s WHERE k=?", 0),
+ row(set(1, 2, 3), tuple(list(1, 2, 3)))
+ );
+ }
+
+ private static String clean(String classname)
+ {
+ return StringUtils.remove(classname, "org.apache.cassandra.db.marshal.");
+ }
+
+ @Test
+ public void testToString()
+ {
+ // set<frozen<list<int>>>
+ SetType t = SetType.getInstance(ListType.getInstance(Int32Type.instance, false), true);
+ assertEquals("SetType(FrozenType(ListType(Int32Type)))", clean(t.toString()));
+ assertEquals("SetType(ListType(Int32Type))", clean(t.toString(true)));
+
+ // frozen<set<list<int>>>
+ t = SetType.getInstance(ListType.getInstance(Int32Type.instance, false), false);
+ assertEquals("FrozenType(SetType(ListType(Int32Type)))", clean(t.toString()));
+ assertEquals("SetType(ListType(Int32Type))", clean(t.toString(true)));
+
+ // map<frozen<list<int>>, int>
+ MapType m = MapType.getInstance(ListType.getInstance(Int32Type.instance, false), Int32Type.instance, true);
+ assertEquals("MapType(FrozenType(ListType(Int32Type)),Int32Type)", clean(m.toString()));
+ assertEquals("MapType(ListType(Int32Type),Int32Type)", clean(m.toString(true)));
+
+ // frozen<map<list<int>, int>>
+ m = MapType.getInstance(ListType.getInstance(Int32Type.instance, false), Int32Type.instance, false);
+ assertEquals("FrozenType(MapType(ListType(Int32Type),Int32Type))", clean(m.toString()));
+ assertEquals("MapType(ListType(Int32Type),Int32Type)", clean(m.toString(true)));
+
+ // tuple<set<int>>
+ List<AbstractType<?>> types = new ArrayList<>();
+ types.add(SetType.getInstance(Int32Type.instance, true));
+ TupleType tuple = new TupleType(types);
+ assertEquals("TupleType(SetType(Int32Type))", clean(tuple.toString()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java b/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java
index 53e7e71..ce935e3 100644
--- a/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java
+++ b/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java
@@ -24,26 +24,30 @@ public class TupleTypeTest extends CQLTester
@Test
public void testTuplePutAndGet() throws Throwable
{
- createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, text, double>>)");
+ String[] valueTypes = {"frozen<tuple<int, text, double>>", "tuple<int, text, double>"};
+ for (String valueType : valueTypes)
+ {
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, t " + valueType + ")");
- execute("INSERT INTO %s (k, t) VALUES (?, ?)", 0, tuple(3, "foo", 3.4));
- execute("INSERT INTO %s (k, t) VALUES (?, ?)", 1, tuple(8, "bar", 0.2));
- assertAllRows(
- row(0, tuple(3, "foo", 3.4)),
- row(1, tuple(8, "bar", 0.2))
- );
+ execute("INSERT INTO %s (k, t) VALUES (?, ?)", 0, tuple(3, "foo", 3.4));
+ execute("INSERT INTO %s (k, t) VALUES (?, ?)", 1, tuple(8, "bar", 0.2));
+ assertAllRows(
+ row(0, tuple(3, "foo", 3.4)),
+ row(1, tuple(8, "bar", 0.2))
+ );
- // nulls
- execute("INSERT INTO %s (k, t) VALUES (?, ?)", 2, tuple(5, null, 3.4));
- assertRows(execute("SELECT * FROM %s WHERE k=?", 2),
- row(2, tuple(5, null, 3.4))
- );
+ // nulls
+ execute("INSERT INTO %s (k, t) VALUES (?, ?)", 2, tuple(5, null, 3.4));
+ assertRows(execute("SELECT * FROM %s WHERE k=?", 2),
+ row(2, tuple(5, null, 3.4))
+ );
- // incomplete tuple
- execute("INSERT INTO %s (k, t) VALUES (?, ?)", 3, tuple(5, "bar"));
- assertRows(execute("SELECT * FROM %s WHERE k=?", 3),
- row(3, tuple(5, "bar"))
- );
+ // incomplete tuple
+ execute("INSERT INTO %s (k, t) VALUES (?, ?)", 3, tuple(5, "bar"));
+ assertRows(execute("SELECT * FROM %s WHERE k=?", 3),
+ row(3, tuple(5, "bar"))
+ );
+ }
}
@Test
@@ -94,10 +98,4 @@ public class TupleTypeTest extends CQLTester
assertInvalidSyntax("INSERT INTO %s (k, t) VALUES (0, ())");
assertInvalid("INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
}
-
- @Test
- public void testNonFrozenTuple() throws Throwable
- {
- assertInvalid("CREATE TABLE wrong (k int PRIMARY KEY, v tuple<int, text>)");
- }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/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
index 18156c3..92990e9 100644
--- a/test/unit/org/apache/cassandra/db/marshal/CollectionTypeTest.java
+++ b/test/unit/org/apache/cassandra/db/marshal/CollectionTypeTest.java
@@ -36,7 +36,7 @@ public class CollectionTypeTest
@Test
public void testListComparison()
{
- ListType<String> lt = ListType.getInstance(UTF8Type.instance);
+ ListType<String> lt = ListType.getInstance(UTF8Type.instance, true);
ByteBuffer[] lists = new ByteBuffer[] {
ByteBufferUtil.EMPTY_BYTE_BUFFER,
@@ -63,7 +63,7 @@ public class CollectionTypeTest
@Test
public void testSetComparison()
{
- SetType<String> st = SetType.getInstance(UTF8Type.instance);
+ SetType<String> st = SetType.getInstance(UTF8Type.instance, true);
ByteBuffer[] sets = new ByteBuffer[] {
ByteBufferUtil.EMPTY_BYTE_BUFFER,
@@ -90,7 +90,7 @@ public class CollectionTypeTest
@Test
public void testMapComparison()
{
- MapType<String, String> mt = MapType.getInstance(UTF8Type.instance, UTF8Type.instance);
+ MapType<String, String> mt = MapType.getInstance(UTF8Type.instance, UTF8Type.instance, true);
ByteBuffer[] maps = new ByteBuffer[] {
ByteBufferUtil.EMPTY_BYTE_BUFFER,
@@ -119,8 +119,8 @@ public class CollectionTypeTest
@Test
public void listSerDerTest()
{
- ListSerializer<String> sls = ListType.getInstance(UTF8Type.instance).getSerializer();
- ListSerializer<Integer> ils = ListType.getInstance(Int32Type.instance).getSerializer();
+ ListSerializer<String> sls = ListType.getInstance(UTF8Type.instance, true).getSerializer();
+ ListSerializer<Integer> ils = ListType.getInstance(Int32Type.instance, true).getSerializer();
List<String> sl = Arrays.asList("Foo", "Bar");
List<Integer> il = Arrays.asList(3, 1, 5);
@@ -143,8 +143,8 @@ public class CollectionTypeTest
@Test
public void setSerDerTest()
{
- SetSerializer<String> sss = SetType.getInstance(UTF8Type.instance).getSerializer();
- SetSerializer<Integer> iss = SetType.getInstance(Int32Type.instance).getSerializer();
+ SetSerializer<String> sss = SetType.getInstance(UTF8Type.instance, true).getSerializer();
+ SetSerializer<Integer> iss = SetType.getInstance(Int32Type.instance, true).getSerializer();
Set<String> ss = new HashSet(){{ add("Foo"); add("Bar"); }};
Set<Integer> is = new HashSet(){{ add(3); add(1); add(5); }};
@@ -167,8 +167,8 @@ public class CollectionTypeTest
@Test
public void setMapDerTest()
{
- MapSerializer<String, String> sms = MapType.getInstance(UTF8Type.instance, UTF8Type.instance).getSerializer();
- MapSerializer<Integer, Integer> ims = MapType.getInstance(Int32Type.instance, Int32Type.instance).getSerializer();
+ MapSerializer<String, String> sms = MapType.getInstance(UTF8Type.instance, UTF8Type.instance, true).getSerializer();
+ MapSerializer<Integer, Integer> ims = MapType.getInstance(Int32Type.instance, Int32Type.instance, true).getSerializer();
Map<String, String> sm = new HashMap(){{ put("Foo", "xxx"); put("Bar", "yyy"); }};
Map<Integer, Integer> im = new HashMap(){{ put(3, 0); put(1, 8); put(5, 2); }};
@@ -187,8 +187,8 @@ public class CollectionTypeTest
// non map value
assertInvalid(sms, UTF8Type.instance.getSerializer().serialize("foo"));
- MapSerializer<Integer, String> sims = MapType.getInstance(Int32Type.instance, UTF8Type.instance).getSerializer();
- MapSerializer<String, Integer> isms = MapType.getInstance(UTF8Type.instance, Int32Type.instance).getSerializer();
+ MapSerializer<Integer, String> sims = MapType.getInstance(Int32Type.instance, UTF8Type.instance, true).getSerializer();
+ MapSerializer<String, Integer> isms = MapType.getInstance(UTF8Type.instance, Int32Type.instance, true).getSerializer();
// only key are invalid
assertInvalid(isms, sb);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/test/unit/org/apache/cassandra/transport/SerDeserTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/transport/SerDeserTest.java b/test/unit/org/apache/cassandra/transport/SerDeserTest.java
index 9b66efb..649f7a2 100644
--- a/test/unit/org/apache/cassandra/transport/SerDeserTest.java
+++ b/test/unit/org/apache/cassandra/transport/SerDeserTest.java
@@ -31,7 +31,6 @@ import org.apache.cassandra.serializers.CollectionSerializer;
import org.apache.cassandra.transport.Event.TopologyChange;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.StatusChange;
-import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
@@ -53,7 +52,7 @@ public class SerDeserTest
public void collectionSerDeserTest(int version) throws Exception
{
// Lists
- ListType<?> lt = ListType.getInstance(Int32Type.instance);
+ ListType<?> lt = ListType.getInstance(Int32Type.instance, true);
List<Integer> l = Arrays.asList(2, 6, 1, 9);
List<ByteBuffer> lb = new ArrayList<>(l.size());
@@ -63,7 +62,7 @@ public class SerDeserTest
assertEquals(l, lt.getSerializer().deserializeForNativeProtocol(CollectionSerializer.pack(lb, lb.size(), version), version));
// Sets
- SetType<?> st = SetType.getInstance(UTF8Type.instance);
+ SetType<?> st = SetType.getInstance(UTF8Type.instance, true);
Set<String> s = new LinkedHashSet<>();
s.addAll(Arrays.asList("bar", "foo", "zee"));
@@ -74,7 +73,7 @@ public class SerDeserTest
assertEquals(s, st.getSerializer().deserializeForNativeProtocol(CollectionSerializer.pack(sb, sb.size(), version), version));
// Maps
- MapType<?, ?> mt = MapType.getInstance(UTF8Type.instance, LongType.instance);
+ MapType<?, ?> mt = MapType.getInstance(UTF8Type.instance, LongType.instance, true);
Map<String, Long> m = new LinkedHashMap<>();
m.put("bar", 12L);
m.put("foo", 42L);
@@ -165,9 +164,9 @@ public class SerDeserTest
public void udtSerDeserTest(int version) throws Exception
{
- ListType<?> lt = ListType.getInstance(Int32Type.instance);
- SetType<?> st = SetType.getInstance(UTF8Type.instance);
- MapType<?, ?> mt = MapType.getInstance(UTF8Type.instance, LongType.instance);
+ ListType<?> lt = ListType.getInstance(Int32Type.instance, true);
+ SetType<?> st = SetType.getInstance(UTF8Type.instance, true);
+ MapType<?, ?> mt = MapType.getInstance(UTF8Type.instance, LongType.instance, true);
UserType udt = new UserType("ks",
bb("myType"),
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/tools/stress/src/org/apache/cassandra/stress/generate/values/Lists.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/generate/values/Lists.java b/tools/stress/src/org/apache/cassandra/stress/generate/values/Lists.java
index 6480d7a..bfa58ea 100644
--- a/tools/stress/src/org/apache/cassandra/stress/generate/values/Lists.java
+++ b/tools/stress/src/org/apache/cassandra/stress/generate/values/Lists.java
@@ -33,7 +33,7 @@ public class Lists extends Generator<List>
public Lists(String name, Generator valueType, GeneratorConfig config)
{
- super(ListType.getInstance(valueType.type), config, name, List.class);
+ super(ListType.getInstance(valueType.type, true), config, name, List.class);
this.valueType = valueType;
buffer = new Object[(int) sizeDistribution.maxValue()];
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/tools/stress/src/org/apache/cassandra/stress/generate/values/Sets.java
----------------------------------------------------------------------
diff --git a/tools/stress/src/org/apache/cassandra/stress/generate/values/Sets.java b/tools/stress/src/org/apache/cassandra/stress/generate/values/Sets.java
index 8246286..5c17b4e 100644
--- a/tools/stress/src/org/apache/cassandra/stress/generate/values/Sets.java
+++ b/tools/stress/src/org/apache/cassandra/stress/generate/values/Sets.java
@@ -32,7 +32,7 @@ public class Sets extends Generator<Set>
public Sets(String name, Generator valueType, GeneratorConfig config)
{
- super(SetType.getInstance(valueType.type), config, name, Set.class);
+ super(SetType.getInstance(valueType.type, true), config, name, Set.class);
this.valueType = valueType;
}
[4/4] cassandra git commit: Merge branch 'cassandra-2.1' into trunk
Posted by ty...@apache.org.
Merge branch 'cassandra-2.1' into trunk
Conflicts:
src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
src/java/org/apache/cassandra/db/marshal/ListType.java
src/java/org/apache/cassandra/db/marshal/TypeParser.java
src/java/org/apache/cassandra/serializers/ListSerializer.java
src/java/org/apache/cassandra/serializers/MapSerializer.java
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/fb4356a3
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/fb4356a3
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/fb4356a3
Branch: refs/heads/trunk
Commit: fb4356a36a3066bf1c35d6f5e5b8472619b47960
Parents: f2e2862 ee55f36
Author: Tyler Hobbs <ty...@datastax.com>
Authored: Tue Nov 11 13:33:31 2014 -0600
Committer: Tyler Hobbs <ty...@datastax.com>
Committed: Tue Nov 11 13:33:31 2014 -0600
----------------------------------------------------------------------
CHANGES.txt | 1 +
bin/cqlsh | 18 +
pylib/cqlshlib/cql3handling.py | 19 +-
.../apache/cassandra/cql3/AbstractMarker.java | 2 +-
src/java/org/apache/cassandra/cql3/CQL3Row.java | 2 +-
.../org/apache/cassandra/cql3/CQL3Type.java | 158 ++--
.../apache/cassandra/cql3/ColumnCondition.java | 275 ++++---
.../org/apache/cassandra/cql3/Constants.java | 2 +-
src/java/org/apache/cassandra/cql3/Cql.g | 14 +-
src/java/org/apache/cassandra/cql3/Lists.java | 64 +-
src/java/org/apache/cassandra/cql3/Maps.java | 61 +-
.../org/apache/cassandra/cql3/Operation.java | 16 +-
src/java/org/apache/cassandra/cql3/Sets.java | 80 +-
src/java/org/apache/cassandra/cql3/Term.java | 6 +
src/java/org/apache/cassandra/cql3/Tuples.java | 13 +-
.../apache/cassandra/cql3/UntypedResultSet.java | 6 +-
.../apache/cassandra/cql3/UpdateParameters.java | 2 +-
.../org/apache/cassandra/cql3/UserTypes.java | 3 +-
.../cql3/statements/AlterTableStatement.java | 39 +-
.../cql3/statements/AlterTypeStatement.java | 18 +-
.../cql3/statements/CreateIndexStatement.java | 24 +-
.../cql3/statements/CreateTableStatement.java | 28 +-
.../cql3/statements/DeleteStatement.java | 2 +-
.../cql3/statements/DropTypeStatement.java | 6 +-
.../cassandra/cql3/statements/IndexTarget.java | 23 +-
.../cassandra/cql3/statements/Restriction.java | 6 +
.../cql3/statements/SelectStatement.java | 84 +-
.../statements/SingleColumnRestriction.java | 24 +
.../org/apache/cassandra/db/CFRowAdder.java | 4 +-
.../db/composites/AbstractCellNameType.java | 4 +-
.../cassandra/db/composites/CellNameType.java | 2 +-
.../db/composites/CompositesBuilder.java | 41 +
.../composites/CompoundSparseCellNameType.java | 5 +-
.../cassandra/db/filter/ExtendedFilter.java | 47 +-
.../cassandra/db/index/SecondaryIndex.java | 6 +-
.../db/index/SecondaryIndexManager.java | 34 +-
.../db/index/SecondaryIndexSearcher.java | 2 +
.../db/index/composites/CompositesIndex.java | 4 +-
.../CompositesIndexOnCollectionValue.java | 2 +-
.../cassandra/db/marshal/AbstractType.java | 18 +
.../cassandra/db/marshal/CollectionType.java | 113 ++-
.../db/marshal/ColumnToCollectionType.java | 2 +-
.../apache/cassandra/db/marshal/FrozenType.java | 62 ++
.../apache/cassandra/db/marshal/ListType.java | 75 +-
.../apache/cassandra/db/marshal/MapType.java | 103 ++-
.../apache/cassandra/db/marshal/SetType.java | 68 +-
.../apache/cassandra/db/marshal/TupleType.java | 9 +-
.../apache/cassandra/db/marshal/TypeParser.java | 33 +-
.../apache/cassandra/db/marshal/UserType.java | 2 +-
.../apache/cassandra/hadoop/pig/CqlStorage.java | 8 +-
.../serializers/CollectionSerializer.java | 24 +-
.../cassandra/serializers/ListSerializer.java | 35 +-
.../cassandra/serializers/MapSerializer.java | 38 +-
.../apache/cassandra/transport/DataType.java | 16 +-
.../org/apache/cassandra/cql3/CQLTester.java | 82 +-
.../cassandra/cql3/ColumnConditionTest.java | 28 +-
.../cassandra/cql3/FrozenCollectionsTest.java | 791 +++++++++++++++++++
.../apache/cassandra/cql3/TupleTypeTest.java | 44 +-
.../db/marshal/CollectionTypeTest.java | 22 +-
.../cassandra/transport/SerDeserTest.java | 13 +-
.../cassandra/stress/generate/values/Lists.java | 2 +-
.../cassandra/stress/generate/values/Sets.java | 2 +-
62 files changed, 2166 insertions(+), 571 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 1b981c6,5b63f48..d17f958
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,36 -1,5 +1,37 @@@
+3.0
+ * Mark sstables as repaired after full repair (CASSANDRA-7586)
+ * Extend Descriptor to include a format value and refactor reader/writer apis (CASSANDRA-7443)
+ * Integrate JMH for microbenchmarks (CASSANDRA-8151)
+ * Keep sstable levels when bootstrapping (CASSANDRA-7460)
+ * Add Sigar library and perform basic OS settings check on startup (CASSANDRA-7838)
+ * Support for aggregation functions (CASSANDRA-4914)
+ * Remove cassandra-cli (CASSANDRA-7920)
+ * Accept dollar quoted strings in CQL (CASSANDRA-7769)
+ * Make assassinate a first class command (CASSANDRA-7935)
+ * Support IN clause on any clustering column (CASSANDRA-4762)
+ * Improve compaction logging (CASSANDRA-7818)
+ * Remove YamlFileNetworkTopologySnitch (CASSANDRA-7917)
+ * Do anticompaction in groups (CASSANDRA-6851)
+ * Support pure user-defined functions (CASSANDRA-7395, 7526, 7562, 7740, 7781, 7929,
+ 7924, 7812, 8063)
+ * Permit configurable timestamps with cassandra-stress (CASSANDRA-7416)
+ * Move sstable RandomAccessReader to nio2, which allows using the
+ FILE_SHARE_DELETE flag on Windows (CASSANDRA-4050)
+ * Remove CQL2 (CASSANDRA-5918)
+ * Add Thrift get_multi_slice call (CASSANDRA-6757)
+ * Optimize fetching multiple cells by name (CASSANDRA-6933)
+ * Allow compilation in java 8 (CASSANDRA-7028)
+ * Make incremental repair default (CASSANDRA-7250)
+ * Enable code coverage thru JaCoCo (CASSANDRA-7226)
+ * Switch external naming of 'column families' to 'tables' (CASSANDRA-4369)
+ * Shorten SSTable path (CASSANDRA-6962)
+ * Use unsafe mutations for most unit tests (CASSANDRA-6969)
+ * Fix race condition during calculation of pending ranges (CASSANDRA-7390)
+ * Fail on very large batch sizes (CASSANDRA-8011)
+ * improve concurrency of repair (CASSANDRA-6455)
+
2.1.3
+ * Support for frozen collections (CASSANDRA-7859)
* Fix overflow on histogram computation (CASSANDRA-8028)
* Have paxos reuse the timestamp generation of normal queries (CASSANDRA-7801)
Merged from 2.0:
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/pylib/cqlshlib/cql3handling.py
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/AbstractMarker.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/ColumnCondition.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/Constants.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/Lists.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/Maps.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/Sets.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/Term.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/Tuples.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/UntypedResultSet.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index b6cbd30,09d1e52..7701cbd
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@@ -698,28 -700,63 +698,43 @@@ public class SelectStatement implement
// we always do a slice for CQL3 tables, so it's ok to ignore them here
assert !isColumnRange();
- CBuilder builder = cfm.comparator.prefixBuilder();
+ CompositesBuilder builder = new CompositesBuilder(cfm.comparator.prefixBuilder(), cfm.comparator);
Iterator<ColumnDefinition> idIter = cfm.clusteringColumns().iterator();
-- for (Restriction r : columnRestrictions)
++ for (int i = 0; i < columnRestrictions.length; i++)
{
++ Restriction r = columnRestrictions[i];
ColumnDefinition def = idIter.next();
assert r != null && !r.isSlice();
- List<ByteBuffer> values = r.values(options);
- if (r.isEQ())
++ if (r.isEQ() || !r.isMultiColumn())
+ {
- ByteBuffer val = r.values(options).get(0);
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
- builder.add(val);
++ List<ByteBuffer> values = r.values(options);
++ if (values.isEmpty())
++ return null;
++ builder.addEachElementToAll(values);
+ }
+ else
+ {
- if (!r.isMultiColumn())
- {
- List<ByteBuffer> values = r.values(options);
- // We have a IN, which we only support for the last column.
- // If compact, just add all values and we're done. Otherwise,
- // for each value of the IN, creates all the columns corresponding to the selection.
- if (values.isEmpty())
- return null;
- SortedSet<CellName> columns = new TreeSet<CellName>(cfm.comparator);
- Iterator<ByteBuffer> iter = values.iterator();
- while (iter.hasNext())
- {
- ByteBuffer val = iter.next();
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
++ // we have a multi-column IN restriction
++ List<List<ByteBuffer>> splitValues = ((MultiColumnRestriction.IN) r).splitValues(options);
++ if (splitValues.isEmpty())
++ return null;
- if (values.isEmpty())
- return null;
- Composite prefix = builder.buildWith(val);
- columns.addAll(addSelectedColumns(prefix));
- }
- return columns;
- }
- else
- {
- // we have a multi-column IN restriction
- List<List<ByteBuffer>> values = ((MultiColumnRestriction.IN) r).splitValues(options);
- TreeSet<CellName> inValues = new TreeSet<>(cfm.comparator);
- for (List<ByteBuffer> components : values)
- {
- for (int i = 0; i < components.size(); i++)
- if (components.get(i) == null)
- throw new InvalidRequestException("Invalid null value in condition for column " + cfm.clusteringColumns().get(i + def.position()));
++ builder.addAllElementsToAll(splitValues);
+
- Composite prefix = builder.buildWith(components);
- inValues.addAll(addSelectedColumns(prefix));
- }
- return inValues;
- }
++ // increment i to skip the remainder of the multicolumn restriction
++ i += splitValues.get(0).size() - 1;
+ }
+
- builder.addEachElementToAll(values);
+ if (builder.containsNull())
+ throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s",
- def.name));
++ def.name));
}
- SortedSet<CellName> columns = new TreeSet<CellName>(cfm.comparator);
+
- return addSelectedColumns(builder.build());
++ SortedSet<CellName> columns = new TreeSet<>(cfm.comparator);
+ for (Composite composite : builder.build())
+ columns.addAll(addSelectedColumns(composite));
-
+ return columns;
}
private SortedSet<CellName> addSelectedColumns(Composite prefix)
@@@ -1891,9 -1965,17 +1919,15 @@@
}
else if (restriction.isIN())
{
- if (!restriction.isMultiColumn() && i != stmt.columnRestrictions.length - 1)
- throw new InvalidRequestException(String.format("Clustering column \"%s\" cannot be restricted by an IN relation", cdef.name));
- else if (stmt.selectACollection())
+ if (stmt.selectACollection())
throw new InvalidRequestException(String.format("Cannot restrict column \"%s\" by IN relation as a collection is selected by the query", cdef.name));
}
+ /*
+ else if (restriction.isContains() && !hasQueriableIndex)
+ {
+ throw new InvalidRequestException(String.format("Cannot restrict column \"%s\" by a CONTAINS relation without a secondary index", cdef.name));
+ }
+ */
previous = cdef;
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/db/composites/CellNameType.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/db/composites/CompositesBuilder.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/composites/CompositesBuilder.java
index 29fe905,0000000..4542ac5
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/composites/CompositesBuilder.java
+++ b/src/java/org/apache/cassandra/db/composites/CompositesBuilder.java
@@@ -1,219 -1,0 +1,260 @@@
+/*
+ * 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.composites;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.cassandra.db.composites.Composite.EOC;
+
+import static java.util.Collections.singletonList;
+
+/**
+ * Builder that allow to build multiple composites at the same time.
+ */
+public final class CompositesBuilder
+{
+ /**
+ * The builder used to build the <code>Composite</code>s.
+ */
+ private final CBuilder builder;
+
+ /**
+ * The comparator used to sort the returned <code>Composite</code>s.
+ */
+ private final Comparator<Composite> comparator;
+
+ /**
+ * The elements of the composites
+ */
+ private final List<List<ByteBuffer>> elementsList = new ArrayList<>();
+
+ /**
+ * The number of elements that still can be added.
+ */
+ private int remaining;
+
+ /**
+ * <code>true</code> if the composites have been build, <code>false</code> otherwise.
+ */
+ private boolean built;
+
+ /**
+ * <code>true</code> if the composites contains some <code>null</code> elements.
+ */
+ private boolean containsNull;
+
+ public CompositesBuilder(CBuilder builder, Comparator<Composite> comparator)
+ {
+ this.builder = builder;
+ this.comparator = comparator;
+ this.remaining = builder.remainingCount();
+ }
+
+ /**
+ * Adds the specified element to all the composites.
+ * <p>
+ * If this builder contains 2 composites: A-B and A-C a call to this method to add D will result in the composites:
+ * A-B-D and A-C-D.
+ * </p>
+ *
+ * @param value the value of the next element
+ * @return this <code>CompositeBuilder</code>
+ */
+ public CompositesBuilder addElementToAll(ByteBuffer value)
+ {
+ checkUpdateable();
+
+ if (isEmpty())
+ elementsList.add(new ArrayList<ByteBuffer>());
+
+ for (int i = 0, m = elementsList.size(); i < m; i++)
+ {
+ if (value == null)
+ containsNull = true;
+
+ elementsList.get(i).add(value);
+ }
+ remaining--;
+ return this;
+ }
+
+ /**
+ * Adds individually each of the specified elements to the end of all of the existing composites.
+ * <p>
+ * If this builder contains 2 composites: A-B and A-C a call to this method to add D and E will result in the 4
+ * composites: A-B-D, A-B-E, A-C-D and A-C-E.
+ * </p>
+ *
+ * @param values the elements to add
+ * @return this <code>CompositeBuilder</code>
+ */
+ public CompositesBuilder addEachElementToAll(List<ByteBuffer> values)
+ {
+ checkUpdateable();
+
+ if (isEmpty())
+ elementsList.add(new ArrayList<ByteBuffer>());
+
+ for (int i = 0, m = elementsList.size(); i < m; i++)
+ {
+ List<ByteBuffer> oldComposite = elementsList.remove(0);
+
+ for (int j = 0, n = values.size(); j < n; j++)
+ {
+ List<ByteBuffer> newComposite = new ArrayList<>(oldComposite);
+ elementsList.add(newComposite);
+
+ ByteBuffer value = values.get(j);
+
+ if (value == null)
+ containsNull = true;
+
+ newComposite.add(values.get(j));
+ }
+ }
+
+ remaining--;
+ return this;
+ }
+
++
++ /**
++ * Adds individually each of the specified list of elements to the end of all of the existing composites.
++ * <p>
++ * If this builder contains 2 composites: A-B and A-C a call to this method to add [[D, E], [F, G]] will result in the 4
++ * composites: A-B-D-E, A-B-F-G, A-C-D-E and A-C-F-G.
++ * </p>
++ *
++ * @param values the elements to add
++ * @return this <code>CompositeBuilder</code>
++ */
++ public CompositesBuilder addAllElementsToAll(List<List<ByteBuffer>> values)
++ {
++ assert !values.isEmpty();
++ checkUpdateable();
++
++ if (isEmpty())
++ elementsList.add(new ArrayList<ByteBuffer>());
++
++ for (int i = 0, m = elementsList.size(); i < m; i++)
++ {
++ List<ByteBuffer> oldComposite = elementsList.remove(0);
++
++ for (int j = 0, n = values.size(); j < n; j++)
++ {
++ List<ByteBuffer> newComposite = new ArrayList<>(oldComposite);
++ elementsList.add(newComposite);
++
++ List<ByteBuffer> value = values.get(j);
++
++ if (value.contains(null))
++ containsNull = true;
++
++ newComposite.addAll(value);
++ }
++ }
++
++ remaining -= values.get(0).size();
++ return this;
++ }
++
+ /**
+ * Returns the number of elements that can be added to the composites.
+ *
+ * @return the number of elements that can be added to the composites.
+ */
+ public int remainingCount()
+ {
+ return remaining;
+ }
+
+ /**
+ * Checks if some elements can still be added to the composites.
+ *
+ * @return <code>true</code> if it is possible to add more elements to the composites, <code>false</code> otherwise.
+ */
+ public boolean hasRemaining()
+ {
+ return remaining > 0;
+ }
+
+ /**
+ * Checks if this builder is empty.
+ *
+ * @return <code>true</code> if this builder is empty, <code>false</code> otherwise.
+ */
+ public boolean isEmpty()
+ {
+ return elementsList.isEmpty();
+ }
+
+ /**
+ * Checks if the composites contains null elements.
+ *
+ * @return <code>true</code> if the composites contains <code>null</code> elements, <code>false</code> otherwise.
+ */
+ public boolean containsNull()
+ {
+ return containsNull;
+ }
+
+ /**
+ * Builds the <code>Composites</code>.
+ *
+ * @return the composites
+ */
+ public List<Composite> build()
+ {
+ return buildWithEOC(EOC.NONE);
+ }
+
+ /**
+ * Builds the <code>Composites</code> with the specified EOC.
+ *
+ * @return the composites
+ */
+ public List<Composite> buildWithEOC(EOC eoc)
+ {
+ built = true;
+
+ if (elementsList.isEmpty())
+ return singletonList(builder.build().withEOC(eoc));
+
+ // Use a TreeSet to sort and eliminate duplicates
+ Set<Composite> set = new TreeSet<Composite>(comparator);
+
+ for (int i = 0, m = elementsList.size(); i < m; i++)
+ {
+ List<ByteBuffer> elements = elementsList.get(i);
+ set.add(builder.buildWith(elements).withEOC(eoc));
+ }
+
+ return new ArrayList<>(set);
+ }
+
+ private void checkUpdateable()
+ {
+ if (!hasRemaining() || built)
+ throw new IllegalStateException("this CompositesBuilder cannot be updated anymore");
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/db/index/SecondaryIndex.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
index 06a1e0e,fbbec1d..10a7ce3
--- a/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
+++ b/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
@@@ -33,7 -34,7 +34,6 @@@ import java.util.concurrent.ConcurrentN
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.Future;
- import org.apache.cassandra.io.sstable.format.SSTableReader;
-import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@@ -53,6 -54,7 +53,8 @@@ import org.apache.cassandra.db.filter.E
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.io.sstable.ReducingKeyIterator;
-import org.apache.cassandra.io.sstable.SSTableReader;
++import org.apache.cassandra.io.sstable.format.SSTableReader;
++import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.concurrent.OpOrder;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/db/marshal/AbstractType.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/db/marshal/TypeParser.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/TypeParser.java
index 37f8708,cdb5679..fed6f7f
--- a/src/java/org/apache/cassandra/db/marshal/TypeParser.java
+++ b/src/java/org/apache/cassandra/db/marshal/TypeParser.java
@@@ -572,7 -584,9 +584,8 @@@ public class TypeParse
{
sb.append(',');
sb.append(ByteBufferUtil.bytesToHex(columnNames.get(i))).append(":");
- sb.append(columnTypes.get(i));
-
+ // omit FrozenType(...) from fields because it is currently implicit
+ sb.append(columnTypes.get(i).toString(true));
}
sb.append(')');
return sb.toString();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/hadoop/pig/CqlStorage.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/src/java/org/apache/cassandra/serializers/MapSerializer.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/serializers/MapSerializer.java
index 7e132b8,bc8e509..8350f66
--- a/src/java/org/apache/cassandra/serializers/MapSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/MapSerializer.java
@@@ -22,6 -22,9 +22,8 @@@ import java.nio.BufferUnderflowExceptio
import java.nio.ByteBuffer;
import java.util.*;
+ import org.apache.cassandra.db.marshal.AbstractType;
+ import org.apache.cassandra.transport.Server;
-import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.Pair;
public class MapSerializer<K, V> extends CollectionSerializer<Map<K, V>>
http://git-wip-us.apache.org/repos/asf/cassandra/blob/fb4356a3/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
[2/4] cassandra git commit: Support for frozen collections
Posted by ty...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
index 7be635f..a17ee92 100644
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
@@ -120,13 +120,12 @@ public class AlterTableStatement extends SchemaAlteringStatement
throw new InvalidRequestException(String.format("Cannot re-add previously dropped counter column %s", columnName));
AbstractType<?> type = validator.getType();
- if (type instanceof CollectionType)
+ if (type.isCollection() && type.isMultiCell())
{
if (!cfm.comparator.supportCollections())
- throw new InvalidRequestException("Cannot use collection types with non-composite PRIMARY KEY");
+ throw new InvalidRequestException("Cannot use non-frozen collections with a non-composite PRIMARY KEY");
if (cfm.isSuper())
- throw new InvalidRequestException("Cannot use collection types with Super column family");
-
+ throw new InvalidRequestException("Cannot use non-frozen collections with super column families");
// If there used to be a collection column with the same name (that has been dropped), it will
// still be appear in the ColumnToCollectionType because or reasons explained on #6276. The same
@@ -151,35 +150,35 @@ public class AlterTableStatement extends SchemaAlteringStatement
case ALTER:
assert columnName != null;
if (def == null)
- throw new InvalidRequestException(String.format("Cell %s was not found in table %s", columnName, columnFamily()));
+ throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily()));
+ AbstractType<?> validatorType = validator.getType();
switch (def.kind)
{
case PARTITION_KEY:
- AbstractType<?> newType = validator.getType();
- if (newType instanceof CounterColumnType)
+ if (validatorType instanceof CounterColumnType)
throw new InvalidRequestException(String.format("counter type is not supported for PRIMARY KEY part %s", columnName));
if (cfm.getKeyValidator() instanceof CompositeType)
{
List<AbstractType<?>> oldTypes = ((CompositeType) cfm.getKeyValidator()).types;
- if (!newType.isValueCompatibleWith(oldTypes.get(def.position())))
+ if (!validatorType.isValueCompatibleWith(oldTypes.get(def.position())))
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
columnName,
oldTypes.get(def.position()).asCQL3Type(),
validator));
List<AbstractType<?>> newTypes = new ArrayList<AbstractType<?>>(oldTypes);
- newTypes.set(def.position(), newType);
+ newTypes.set(def.position(), validatorType);
cfm.keyValidator(CompositeType.getInstance(newTypes));
}
else
{
- if (!newType.isValueCompatibleWith(cfm.getKeyValidator()))
+ if (!validatorType.isValueCompatibleWith(cfm.getKeyValidator()))
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
columnName,
cfm.getKeyValidator().asCQL3Type(),
validator));
- cfm.keyValidator(newType);
+ cfm.keyValidator(validatorType);
}
break;
case CLUSTERING_COLUMN:
@@ -187,22 +186,22 @@ public class AlterTableStatement extends SchemaAlteringStatement
// Note that CFMetaData.validateCompatibility already validate the change we're about to do. However, the error message it
// sends is a bit cryptic for a CQL3 user, so validating here for a sake of returning a better error message
// Do note that we need isCompatibleWith here, not just isValueCompatibleWith.
- if (!validator.getType().isCompatibleWith(oldType))
+ if (!validatorType.isCompatibleWith(oldType))
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are not order-compatible.",
columnName,
oldType.asCQL3Type(),
validator));
- cfm.comparator = cfm.comparator.setSubtype(def.position(), validator.getType());
+ cfm.comparator = cfm.comparator.setSubtype(def.position(), validatorType);
break;
case COMPACT_VALUE:
// See below
- if (!validator.getType().isValueCompatibleWith(cfm.getDefaultValidator()))
+ if (!validatorType.isValueCompatibleWith(cfm.getDefaultValidator()))
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
columnName,
cfm.getDefaultValidator().asCQL3Type(),
validator));
- cfm.defaultValidator(validator.getType());
+ cfm.defaultValidator(validatorType);
break;
case REGULAR:
case STATIC:
@@ -211,23 +210,23 @@ public class AlterTableStatement extends SchemaAlteringStatement
// allow it for CQL3 (see #5882) so validating it explicitly here. We only care about value compatibility
// though since we won't compare values (except when there is an index, but that is validated by
// ColumnDefinition already).
- if (!validator.getType().isValueCompatibleWith(def.type))
+ if (!validatorType.isValueCompatibleWith(def.type))
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
columnName,
def.type.asCQL3Type(),
validator));
// For collections, if we alter the type, we need to update the comparator too since it includes
- // the type too (note that isValueCompatibleWith above has validated that the need type don't really
+ // the type too (note that isValueCompatibleWith above has validated that the new type doesn't
// change the underlying sorting order, but we still don't want to have a discrepancy between the type
// in the comparator and the one in the ColumnDefinition as that would be dodgy).
- if (validator.getType() instanceof CollectionType)
- cfm.comparator = cfm.comparator.addOrUpdateCollection(def.name, (CollectionType)validator.getType());
+ if (validatorType.isCollection() && validatorType.isMultiCell())
+ cfm.comparator = cfm.comparator.addOrUpdateCollection(def.name, (CollectionType)validatorType);
break;
}
// In any case, we update the column definition
- cfm.addOrReplaceColumnDefinition(def.withNewType(validator.getType()));
+ cfm.addOrReplaceColumnDefinition(def.withNewType(validatorType));
break;
case DROP:
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
index cfdd65f..576011f 100644
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
@@ -214,23 +214,27 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement
{
if (type instanceof ListType)
{
- AbstractType<?> t = updateWith(((ListType)type).elements, keyspace, toReplace, updated);
- return t == null ? null : ListType.getInstance(t);
+ AbstractType<?> t = updateWith(((ListType)type).getElementsType(), keyspace, toReplace, updated);
+ if (t == null)
+ return null;
+ return ListType.getInstance(t, type.isMultiCell());
}
else if (type instanceof SetType)
{
- AbstractType<?> t = updateWith(((SetType)type).elements, keyspace, toReplace, updated);
- return t == null ? null : SetType.getInstance(t);
+ AbstractType<?> t = updateWith(((SetType)type).getElementsType(), keyspace, toReplace, updated);
+ if (t == null)
+ return null;
+ return SetType.getInstance(t, type.isMultiCell());
}
else
{
assert type instanceof MapType;
MapType mt = (MapType)type;
- AbstractType<?> k = updateWith(mt.keys, keyspace, toReplace, updated);
- AbstractType<?> v = updateWith(mt.values, keyspace, toReplace, updated);
+ AbstractType<?> k = updateWith(mt.getKeysType(), keyspace, toReplace, updated);
+ AbstractType<?> v = updateWith(mt.getValuesType(), keyspace, toReplace, updated);
if (k == null && v == null)
return null;
- return MapType.getInstance(k == null ? mt.keys : k, v == null ? mt.values : v);
+ return MapType.getInstance(k == null ? mt.getKeysType() : k, v == null ? mt.getValuesType() : v, type.isMultiCell());
}
}
else
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
index c93adc2..3032897 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
@@ -79,8 +79,24 @@ public class CreateIndexStatement extends SchemaAlteringStatement
throw new InvalidRequestException("No column definition found for column " + target.column);
boolean isMap = cd.type instanceof MapType;
- if (target.isCollectionKeys && !isMap)
- throw new InvalidRequestException("Cannot create index on keys of column " + target + " with non map type");
+ boolean isFrozenCollection = cd.type.isCollection() && !cd.type.isMultiCell();
+ if (target.isCollectionKeys)
+ {
+ if (!isMap)
+ throw new InvalidRequestException("Cannot create index on keys of column " + target + " with non-map type");
+ if (!cd.type.isMultiCell())
+ throw new InvalidRequestException("Cannot create index on keys of frozen<map> column " + target);
+ }
+ else if (target.isFullCollection)
+ {
+ if (!isFrozenCollection)
+ throw new InvalidRequestException("full() indexes can only be created on frozen collections");
+ }
+ else if (isFrozenCollection)
+ {
+ throw new InvalidRequestException("Frozen collections currently only support full-collection indexes. " +
+ "For example, 'CREATE INDEX ON <table>(full(<columnName>))'.");
+ }
if (cd.getIndexType() != null)
{
@@ -116,7 +132,7 @@ public class CreateIndexStatement extends SchemaAlteringStatement
throw new InvalidRequestException("Secondary indexes are not allowed on static columns");
if (cd.kind == ColumnDefinition.Kind.PARTITION_KEY && cd.isOnAllComponents())
- throw new InvalidRequestException(String.format("Cannot add secondary index to already primarily indexed column %s", target.column));
+ throw new InvalidRequestException(String.format("Cannot create secondary index on partition key column %s", target.column));
}
public boolean announceMigration(boolean isLocalOnly) throws RequestValidationException
@@ -139,7 +155,7 @@ public class CreateIndexStatement extends SchemaAlteringStatement
// For now, we only allow indexing values for collections, but we could later allow
// to also index map keys, so we record that this is the values we index to make our
// lives easier then.
- if (cd.type.isCollection())
+ if (cd.type.isCollection() && cd.type.isMultiCell())
options = ImmutableMap.of(target.isCollectionKeys ? SecondaryIndex.INDEX_KEYS_OPTION_NAME
: SecondaryIndex.INDEX_VALUES_OPTION_NAME, "");
cd.setIndexType(IndexType.COMPOSITES, options);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
index 672bb50..5f70ab8 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateTableStatement.java
@@ -199,16 +199,16 @@ public class CreateTableStatement extends SchemaAlteringStatement
CreateTableStatement stmt = new CreateTableStatement(cfName, properties, ifNotExists, staticColumns);
- Map<ByteBuffer, CollectionType> definedCollections = null;
+ Map<ByteBuffer, CollectionType> definedMultiCellCollections = null;
for (Map.Entry<ColumnIdentifier, CQL3Type.Raw> entry : definitions.entrySet())
{
ColumnIdentifier id = entry.getKey();
CQL3Type pt = entry.getValue().prepare(keyspace());
- if (pt.isCollection())
+ if (pt.isCollection() && ((CollectionType) pt.getType()).isMultiCell())
{
- if (definedCollections == null)
- definedCollections = new HashMap<ByteBuffer, CollectionType>();
- definedCollections.put(id.bytes, (CollectionType)pt.getType());
+ if (definedMultiCellCollections == null)
+ definedMultiCellCollections = new HashMap<>();
+ definedMultiCellCollections.put(id.bytes, (CollectionType) pt.getType());
}
stmt.columns.put(id, pt.getType()); // we'll remove what is not a column below
}
@@ -246,16 +246,16 @@ public class CreateTableStatement extends SchemaAlteringStatement
if (stmt.columns.isEmpty())
throw new InvalidRequestException("No definition found that is not part of the PRIMARY KEY");
- if (definedCollections != null)
- throw new InvalidRequestException("Collection types are not supported with COMPACT STORAGE");
+ if (definedMultiCellCollections != null)
+ throw new InvalidRequestException("Non-frozen collection types are not supported with COMPACT STORAGE");
stmt.comparator = new SimpleSparseCellNameType(UTF8Type.instance);
}
else
{
- stmt.comparator = definedCollections == null
+ stmt.comparator = definedMultiCellCollections == null
? new CompoundSparseCellNameType(Collections.<AbstractType<?>>emptyList())
- : new CompoundSparseCellNameType.WithCollection(Collections.<AbstractType<?>>emptyList(), ColumnToCollectionType.getInstance(definedCollections));
+ : new CompoundSparseCellNameType.WithCollection(Collections.<AbstractType<?>>emptyList(), ColumnToCollectionType.getInstance(definedMultiCellCollections));
}
}
else
@@ -264,7 +264,7 @@ public class CreateTableStatement extends SchemaAlteringStatement
// standard "dynamic" CF, otherwise it's a composite
if (useCompactStorage && columnAliases.size() == 1)
{
- if (definedCollections != null)
+ if (definedMultiCellCollections != null)
throw new InvalidRequestException("Collection types are not supported with COMPACT STORAGE");
ColumnIdentifier alias = columnAliases.get(0);
@@ -294,16 +294,16 @@ public class CreateTableStatement extends SchemaAlteringStatement
if (useCompactStorage)
{
- if (definedCollections != null)
+ if (definedMultiCellCollections != null)
throw new InvalidRequestException("Collection types are not supported with COMPACT STORAGE");
stmt.comparator = new CompoundDenseCellNameType(types);
}
else
{
- stmt.comparator = definedCollections == null
+ stmt.comparator = definedMultiCellCollections == null
? new CompoundSparseCellNameType(types)
- : new CompoundSparseCellNameType.WithCollection(types, ColumnToCollectionType.getInstance(definedCollections));
+ : new CompoundSparseCellNameType.WithCollection(types, ColumnToCollectionType.getInstance(definedMultiCellCollections));
}
}
}
@@ -385,7 +385,7 @@ public class CreateTableStatement extends SchemaAlteringStatement
AbstractType type = columns.get(t);
if (type == null)
throw new InvalidRequestException(String.format("Unknown definition %s referenced in PRIMARY KEY", t));
- if (type instanceof CollectionType)
+ if (type.isCollection() && type.isMultiCell())
throw new InvalidRequestException(String.format("Invalid collection type for PRIMARY KEY component %s", t));
columns.remove(t);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
index b49f60b..33c61e7 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
@@ -56,7 +56,7 @@ public class DeleteStatement extends ModificationStatement
// However, if we delete only static colums, it's fine since we won't really use the prefix anyway.
for (Operation deletion : deletions)
if (!deletion.column.isStatic())
- throw new InvalidRequestException(String.format("Missing mandatory PRIMARY KEY part %s since %s specified", getFirstEmptyKey(), deletion.column.name));
+ throw new InvalidRequestException(String.format("Primary key column '%s' must be specified in order to delete column '%s'", getFirstEmptyKey().name, deletion.column.name));
}
if (deletions.isEmpty())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
index 8bcaaf6..a3b82a4 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropTypeStatement.java
@@ -117,11 +117,11 @@ public class DropTypeStatement extends SchemaAlteringStatement
else if (toCheck instanceof CollectionType)
{
if (toCheck instanceof ListType)
- return isUsedBy(((ListType)toCheck).elements);
+ return isUsedBy(((ListType)toCheck).getElementsType());
else if (toCheck instanceof SetType)
- return isUsedBy(((SetType)toCheck).elements);
+ return isUsedBy(((SetType)toCheck).getElementsType());
else
- return isUsedBy(((MapType)toCheck).keys) || isUsedBy(((MapType)toCheck).keys);
+ return isUsedBy(((MapType)toCheck).getKeysType()) || isUsedBy(((MapType)toCheck).getKeysType());
}
return false;
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java b/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java
index dc77bcc..eeee907 100644
--- a/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java
+++ b/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java
@@ -24,37 +24,46 @@ public class IndexTarget
{
public final ColumnIdentifier column;
public final boolean isCollectionKeys;
+ public final boolean isFullCollection;
- private IndexTarget(ColumnIdentifier column, boolean isCollectionKeys)
+ private IndexTarget(ColumnIdentifier column, boolean isCollectionKeys, boolean isFullCollection)
{
this.column = column;
this.isCollectionKeys = isCollectionKeys;
+ this.isFullCollection = isFullCollection;
}
public static class Raw
{
private final ColumnIdentifier.Raw column;
- public final boolean isCollectionKeys;
+ private final boolean isCollectionKeys;
+ private final boolean isFullCollection;
- private Raw(ColumnIdentifier.Raw column, boolean isCollectionKeys)
+ private Raw(ColumnIdentifier.Raw column, boolean isCollectionKeys, boolean isFullCollection)
{
this.column = column;
this.isCollectionKeys = isCollectionKeys;
+ this.isFullCollection = isFullCollection;
}
- public static Raw of(ColumnIdentifier.Raw c)
+ public static Raw valuesOf(ColumnIdentifier.Raw c)
{
- return new Raw(c, false);
+ return new Raw(c, false, false);
}
public static Raw keysOf(ColumnIdentifier.Raw c)
{
- return new Raw(c, true);
+ return new Raw(c, true, false);
+ }
+
+ public static Raw fullCollection(ColumnIdentifier.Raw c)
+ {
+ return new Raw(c, false, true);
}
public IndexTarget prepare(CFMetaData cfm)
{
- return new IndexTarget(column.prepare(cfm), isCollectionKeys);
+ return new IndexTarget(column.prepare(cfm), isCollectionKeys, isFullCollection);
}
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/statements/Restriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/Restriction.java b/src/java/org/apache/cassandra/cql3/statements/Restriction.java
index 5307cbb..659ed95 100644
--- a/src/java/org/apache/cassandra/cql3/statements/Restriction.java
+++ b/src/java/org/apache/cassandra/cql3/statements/Restriction.java
@@ -40,6 +40,12 @@ public interface Restriction
public boolean isContains();
public boolean isMultiColumn();
+ /**
+ * Returns true if, when applied to a clustering column, this restriction can be handled through one or more slices
+ * alone without filtering. For example, EQ restrictions can be represented as a slice, but CONTAINS cannot.
+ */
+ public boolean canEvaluateWithSlices();
+
// Not supported by Slice, but it's convenient to have here
public List<ByteBuffer> values(QueryOptions options) throws InvalidRequestException;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/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 84cbdc0..09d1e52 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -707,33 +707,52 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
ColumnDefinition def = idIter.next();
assert r != null && !r.isSlice();
- List<ByteBuffer> values = r.values(options);
- if (values.size() == 1)
+ if (r.isEQ())
{
- ByteBuffer val = values.get(0);
+ ByteBuffer val = r.values(options).get(0);
if (val == null)
throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
builder.add(val);
}
else
{
- // We have a IN, which we only support for the last column.
- // If compact, just add all values and we're done. Otherwise,
- // for each value of the IN, creates all the columns corresponding to the selection.
- if (values.isEmpty())
- return null;
- SortedSet<CellName> columns = new TreeSet<CellName>(cfm.comparator);
- Iterator<ByteBuffer> iter = values.iterator();
- while (iter.hasNext())
+ if (!r.isMultiColumn())
{
- ByteBuffer val = iter.next();
- if (val == null)
- throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
+ List<ByteBuffer> values = r.values(options);
+ // We have a IN, which we only support for the last column.
+ // If compact, just add all values and we're done. Otherwise,
+ // for each value of the IN, creates all the columns corresponding to the selection.
+ if (values.isEmpty())
+ return null;
+ SortedSet<CellName> columns = new TreeSet<CellName>(cfm.comparator);
+ Iterator<ByteBuffer> iter = values.iterator();
+ while (iter.hasNext())
+ {
+ ByteBuffer val = iter.next();
+ if (val == null)
+ throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", def.name));
+
+ Composite prefix = builder.buildWith(val);
+ columns.addAll(addSelectedColumns(prefix));
+ }
+ return columns;
+ }
+ else
+ {
+ // we have a multi-column IN restriction
+ List<List<ByteBuffer>> values = ((MultiColumnRestriction.IN) r).splitValues(options);
+ TreeSet<CellName> inValues = new TreeSet<>(cfm.comparator);
+ for (List<ByteBuffer> components : values)
+ {
+ for (int i = 0; i < components.size(); i++)
+ if (components.get(i) == null)
+ throw new InvalidRequestException("Invalid null value in condition for column " + cfm.clusteringColumns().get(i + def.position()));
- Composite prefix = builder.buildWith(val);
- columns.addAll(addSelectedColumns(prefix));
+ Composite prefix = builder.buildWith(components);
+ inValues.addAll(addSelectedColumns(prefix));
+ }
+ return inValues;
}
- return columns;
}
}
@@ -778,6 +797,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
}
}
+ /** Returns true if a non-frozen collection is selected, false otherwise. */
private boolean selectACollection()
{
if (!cfm.comparator.hasCollections())
@@ -785,7 +805,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
for (ColumnDefinition def : selection.getColumns())
{
- if (def.type instanceof CollectionType)
+ if (def.type.isCollection() && def.type.isMultiCell())
return true;
}
@@ -830,7 +850,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
// But if the actual comparator itself is reversed, we must inversed the bounds too.
Bound b = isReversed == isReversedType(def) ? bound : Bound.reverse(bound);
Restriction r = restrictions[def.position()];
- if (isNullRestriction(r, b))
+ if (isNullRestriction(r, b) || !r.canEvaluateWithSlices())
{
// There wasn't any non EQ relation on that key, we select all records having the preceding component as prefix.
// For composites, if there was preceding component and we're computing the end, we must change the last component
@@ -846,6 +866,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
}
else
{
+ // IN or EQ
List<ByteBuffer> values = r.values(options);
if (values.size() != 1)
{
@@ -1088,7 +1109,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
expressions.add(new IndexExpression(def.name.bytes, Operator.EQ, value));
}
}
-
+
if (usesSecondaryIndexing)
{
ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(columnFamily());
@@ -1258,13 +1279,13 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
return;
}
- if (def.type.isCollection())
+ if (def.type.isMultiCell())
{
- List<Cell> collection = row.getCollection(def.name);
- ByteBuffer value = collection == null
+ List<Cell> cells = row.getMultiCellColumn(def.name);
+ ByteBuffer buffer = cells == null
? null
- : ((CollectionType)def.type).serializeForNativeProtocol(collection, options.getProtocolVersion());
- result.add(value);
+ : ((CollectionType)def.type).serializeForNativeProtocol(cells, options.getProtocolVersion());
+ result.add(buffer);
return;
}
@@ -1470,16 +1491,26 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
stmt.usesSecondaryIndexing = true;
if (!stmt.usesSecondaryIndexing)
- stmt.restrictedColumns.removeAll(cfm.clusteringColumns());
+ {
+ for (ColumnDefinition def : cfm.clusteringColumns())
+ {
+ // Remove clustering column restrictions that can be handled by slices; the remainder will be
+ // handled by filters (which may require a secondary index).
+ Restriction restriction = stmt.columnRestrictions[def.position()];
+ if (restriction != null)
+ {
+ if (restriction.canEvaluateWithSlices())
+ stmt.restrictedColumns.remove(def);
+ else
+ stmt.usesSecondaryIndexing = true;
+ }
+ }
+ }
// Even if usesSecondaryIndexing is false at this point, we'll still have to use one if
- // there is restrictions not covered by the PK.
+ // there are restrictions not covered by the PK.
if (!stmt.metadataRestrictions.isEmpty())
- {
- if (!hasQueriableIndex)
- throw new InvalidRequestException("No indexed columns present in by-columns clause with Equal operator");
stmt.usesSecondaryIndexing = true;
- }
if (stmt.usesSecondaryIndexing)
validateSecondaryIndexSelections(stmt);
@@ -1510,6 +1541,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
SecondaryIndex index = indexManager.getIndexForColumn(def.name.bytes);
if (index != null && index.supportsOperator(relation.operator()))
return new boolean[]{true, def.kind == ColumnDefinition.Kind.CLUSTERING_COLUMN};
+
return new boolean[]{false, false};
}
@@ -1692,8 +1724,8 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
StorageService.getPartitioner().getTokenValidator());
}
- // We don't support relations against entire collections, like "numbers = {1, 2, 3}"
- if (receiver.type.isCollection() && !(newRel.operator().equals(Operator.CONTAINS_KEY) || newRel.operator() == Operator.CONTAINS))
+ // We don't support relations against entire collections (unless they're frozen), like "numbers = {1, 2, 3}"
+ if (receiver.type.isCollection() && receiver.type.isMultiCell() && !(newRel.operator() == Operator.CONTAINS_KEY || newRel.operator() == Operator.CONTAINS))
{
throw new InvalidRequestException(String.format("Collection column '%s' (%s) cannot be restricted by a '%s' relation",
def.name, receiver.type.asCQL3Type(), newRel.operator()));
@@ -1760,7 +1792,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
break;
case CONTAINS_KEY:
if (!(receiver.type instanceof MapType))
- throw new InvalidRequestException(String.format("Cannot use CONTAINS_KEY on non-map column %s", def.name));
+ throw new InvalidRequestException(String.format("Cannot use CONTAINS KEY on non-map column %s", def.name));
// Fallthrough on purpose
case CONTAINS:
{
@@ -1771,6 +1803,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
existingRestriction = new SingleColumnRestriction.Contains();
else if (!existingRestriction.isContains())
throw new InvalidRequestException(String.format("Collection column %s can only be restricted by CONTAINS or CONTAINS KEY", def.name));
+
boolean isKey = newRel.operator() == Operator.CONTAINS_KEY;
receiver = makeCollectionReceiver(receiver, isKey);
Term t = newRel.getValue().prepare(keyspace(), receiver);
@@ -1937,6 +1970,12 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
else if (stmt.selectACollection())
throw new InvalidRequestException(String.format("Cannot restrict column \"%s\" by IN relation as a collection is selected by the query", cdef.name));
}
+ /*
+ else if (restriction.isContains() && !hasQueriableIndex)
+ {
+ throw new InvalidRequestException(String.format("Cannot restrict column \"%s\" by a CONTAINS relation without a secondary index", cdef.name));
+ }
+ */
previous = cdef;
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java b/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java
index 7f8156e..b1c6ccc 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SingleColumnRestriction.java
@@ -73,6 +73,11 @@ public abstract class SingleColumnRestriction implements Restriction
return onToken;
}
+ public boolean canEvaluateWithSlices()
+ {
+ return true;
+ }
+
@Override
public String toString()
{
@@ -127,6 +132,11 @@ public abstract class SingleColumnRestriction implements Restriction
return false;
}
+ public boolean canEvaluateWithSlices()
+ {
+ return true;
+ }
+
@Override
public String toString()
{
@@ -181,6 +191,11 @@ public abstract class SingleColumnRestriction implements Restriction
return false;
}
+ public boolean canEvaluateWithSlices()
+ {
+ return true;
+ }
+
@Override
public String toString()
{
@@ -231,6 +246,11 @@ public abstract class SingleColumnRestriction implements Restriction
return onToken;
}
+ public boolean canEvaluateWithSlices()
+ {
+ return true;
+ }
+
/** Returns true if the start or end bound (depending on the argument) is set, false otherwise */
public boolean hasBound(Bound b)
{
@@ -412,6 +432,10 @@ public abstract class SingleColumnRestriction implements Restriction
return false;
}
+ public boolean canEvaluateWithSlices()
+ {
+ return false;
+ }
@Override
public String toString()
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/CFRowAdder.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/CFRowAdder.java b/src/java/org/apache/cassandra/db/CFRowAdder.java
index dfe49ee..3ff9171 100644
--- a/src/java/org/apache/cassandra/db/CFRowAdder.java
+++ b/src/java/org/apache/cassandra/db/CFRowAdder.java
@@ -64,7 +64,7 @@ public class CFRowAdder
public CFRowAdder resetCollection(String cql3ColumnName)
{
ColumnDefinition def = getDefinition(cql3ColumnName);
- assert def.type.isCollection();
+ assert def.type.isCollection() && def.type.isMultiCell();
Composite name = cf.getComparator().create(prefix, def);
cf.addAtom(new RangeTombstone(name.start(), name.end(), timestamp - 1, ldt));
return this;
@@ -75,7 +75,7 @@ public class CFRowAdder
ColumnDefinition def = getDefinition(cql3ColumnName);
assert def.type instanceof MapType;
MapType mt = (MapType)def.type;
- CellName name = cf.getComparator().create(prefix, def, mt.keys.decompose(key));
+ CellName name = cf.getComparator().create(prefix, def, mt.getKeysType().decompose(key));
return add(name, def, value);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/composites/AbstractCellNameType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/composites/AbstractCellNameType.java b/src/java/org/apache/cassandra/db/composites/AbstractCellNameType.java
index e33cc63..c62f890 100644
--- a/src/java/org/apache/cassandra/db/composites/AbstractCellNameType.java
+++ b/src/java/org/apache/cassandra/db/composites/AbstractCellNameType.java
@@ -304,7 +304,7 @@ public abstract class AbstractCellNameType extends AbstractCType implements Cell
return cell;
}
- public List<Cell> getCollection(ColumnIdentifier name)
+ public List<Cell> getMultiCellColumn(ColumnIdentifier name)
{
return null;
}
@@ -446,7 +446,7 @@ public abstract class AbstractCellNameType extends AbstractCType implements Cell
return columns == null ? null : columns.get(name);
}
- public List<Cell> getCollection(ColumnIdentifier name)
+ public List<Cell> getMultiCellColumn(ColumnIdentifier name)
{
return collections == null ? null : collections.get(name);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/composites/CellNameType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/composites/CellNameType.java b/src/java/org/apache/cassandra/db/composites/CellNameType.java
index 4f45d41..1e87296 100644
--- a/src/java/org/apache/cassandra/db/composites/CellNameType.java
+++ b/src/java/org/apache/cassandra/db/composites/CellNameType.java
@@ -95,7 +95,7 @@ public interface CellNameType extends CType
public boolean supportCollections();
/**
- * The type of the collections (or null if the type has not collections).
+ * The type of the collections (or null if the type does not have any non-frozen collections).
*/
public ColumnToCollectionType collectionType();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/composites/CompoundSparseCellNameType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/composites/CompoundSparseCellNameType.java b/src/java/org/apache/cassandra/db/composites/CompoundSparseCellNameType.java
index 27b3271..c88c6f4 100644
--- a/src/java/org/apache/cassandra/db/composites/CompoundSparseCellNameType.java
+++ b/src/java/org/apache/cassandra/db/composites/CompoundSparseCellNameType.java
@@ -24,10 +24,7 @@ import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.cql3.CQL3Row;
import org.apache.cassandra.cql3.ColumnIdentifier;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.db.marshal.CollectionType;
-import org.apache.cassandra.db.marshal.ColumnToCollectionType;
-import org.apache.cassandra.db.marshal.UTF8Type;
+import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.memory.AbstractAllocator;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/filter/ExtendedFilter.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/filter/ExtendedFilter.java b/src/java/org/apache/cassandra/db/filter/ExtendedFilter.java
index 2d56bfa..6f2760a 100644
--- a/src/java/org/apache/cassandra/db/filter/ExtendedFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/ExtendedFilter.java
@@ -40,9 +40,7 @@ import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.db.IndexExpression;
import org.apache.cassandra.db.composites.CellName;
import org.apache.cassandra.db.composites.Composite;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.db.marshal.CollectionType;
-import org.apache.cassandra.db.marshal.CompositeType;
+import org.apache.cassandra.db.marshal.*;
/**
* Extends a column filter (IFilter) to include a number of IndexExpression.
@@ -324,7 +322,7 @@ public abstract class ExtendedFilter
}
else
{
- if (def.type.isCollection())
+ if (def.type.isCollection() && def.type.isMultiCell())
{
if (!collectionSatisfies(def, data, prefix, expression, collectionElement))
return false;
@@ -338,16 +336,49 @@ public abstract class ExtendedFilter
if (dataValue == null)
return false;
- int v = validator.compare(dataValue, expression.value);
- if (!satisfies(v, expression.operator))
- return false;
+ if (expression.operator == Operator.CONTAINS)
+ {
+ assert def != null && def.type.isCollection() && !def.type.isMultiCell();
+ CollectionType type = (CollectionType)def.type;
+ switch (type.kind)
+ {
+ case LIST:
+ ListType<?> listType = (ListType)def.type;
+ if (!listType.getSerializer().deserialize(dataValue).contains(listType.getElementsType().getSerializer().deserialize(expression.value)))
+ return false;
+ break;
+ case SET:
+ SetType<?> setType = (SetType)def.type;
+ if (!setType.getSerializer().deserialize(dataValue).contains(setType.getElementsType().getSerializer().deserialize(expression.value)))
+ return false;
+ break;
+ case MAP:
+ MapType<?,?> mapType = (MapType)def.type;
+ if (!mapType.getSerializer().deserialize(dataValue).containsValue(mapType.getValuesType().getSerializer().deserialize(expression.value)))
+ return false;
+ break;
+ }
+ }
+ else if (expression.operator == Operator.CONTAINS_KEY)
+ {
+ assert def != null && def.type.isCollection() && !def.type.isMultiCell() && def.type instanceof MapType;
+ MapType<?,?> mapType = (MapType)def.type;
+ if (mapType.getSerializer().getSerializedValue(dataValue, expression.value, mapType.getKeysType()) == null)
+ return false;
+ }
+ else
+ {
+ int v = validator.compare(dataValue, expression.value);
+ if (!satisfies(v, expression.operator))
+ return false;
+ }
}
return true;
}
private static boolean collectionSatisfies(ColumnDefinition def, ColumnFamily data, Composite prefix, IndexExpression expr, ByteBuffer collectionElement)
{
- assert def.type.isCollection();
+ assert def.type.isCollection() && def.type.isMultiCell();
CollectionType type = (CollectionType)def.type;
if (expr.isContains())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/index/SecondaryIndex.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndex.java b/src/java/org/apache/cassandra/db/index/SecondaryIndex.java
index 044912f..62b9e4c 100644
--- a/src/java/org/apache/cassandra/db/index/SecondaryIndex.java
+++ b/src/java/org/apache/cassandra/db/index/SecondaryIndex.java
@@ -35,11 +35,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.cql3.Operator;
-import org.apache.cassandra.db.BufferDecoratedKey;
-import org.apache.cassandra.db.Cell;
-import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.db.DecoratedKey;
-import org.apache.cassandra.db.SystemKeyspace;
+import org.apache.cassandra.db.*;
import org.apache.cassandra.db.compaction.CompactionManager;
import org.apache.cassandra.db.composites.CellName;
import org.apache.cassandra.db.composites.CellNameType;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java b/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
index 7f8e845..fbbec1d 100644
--- a/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
+++ b/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
@@ -18,6 +18,7 @@
package org.apache.cassandra.db.index;
import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -33,6 +34,7 @@ import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.Future;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -585,6 +587,7 @@ public class SecondaryIndexManager
}
// Validate
+ boolean haveSupportedIndexLookup = false;
for (Map.Entry<String, Set<IndexExpression>> expressions : expressionsByIndexType.entrySet())
{
Set<ByteBuffer> columns = columnsByIndexType.get(expressions.getKey());
@@ -593,8 +596,37 @@ public class SecondaryIndexManager
for (IndexExpression expression : expressions.getValue())
{
searcher.validate(expression);
+ haveSupportedIndexLookup |= secondaryIndex.supportsOperator(expression.operator);
}
}
+
+ if (!haveSupportedIndexLookup)
+ {
+ // build the error message
+ int i = 0;
+ StringBuilder sb = new StringBuilder("No secondary indexes on the restricted columns support the provided operators: ");
+ for (Map.Entry<String, Set<IndexExpression>> expressions : expressionsByIndexType.entrySet())
+ {
+ for (IndexExpression expression : expressions.getValue())
+ {
+ if (i++ > 0)
+ sb.append(", ");
+ sb.append("'");
+ String columnName;
+ try
+ {
+ columnName = ByteBufferUtil.string(expression.column);
+ }
+ catch (CharacterCodingException ex)
+ {
+ columnName = "<unprintable>";
+ }
+ sb.append(columnName).append(" ").append(expression.operator).append(" <value>").append("'");
+ }
+ }
+
+ throw new InvalidRequestException(sb.toString());
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java b/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
index b9ccd8e..93e0643 100644
--- a/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
+++ b/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
@@ -18,12 +18,14 @@
package org.apache.cassandra.db.index;
import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
import java.util.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.filter.ExtendedFilter;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.tracing.Tracing;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
public abstract class SecondaryIndexSearcher
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java b/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java
index 410ea83..ec965fd 100644
--- a/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java
+++ b/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java
@@ -58,7 +58,7 @@ public abstract class CompositesIndex extends AbstractSimplePerColumnSecondaryIn
public static CompositesIndex create(ColumnDefinition cfDef)
{
- if (cfDef.type.isCollection())
+ if (cfDef.type.isCollection() && cfDef.type.isMultiCell())
{
switch (((CollectionType)cfDef.type).kind)
{
@@ -90,7 +90,7 @@ public abstract class CompositesIndex extends AbstractSimplePerColumnSecondaryIn
// Check SecondaryIndex.getIndexComparator if you want to know why this is static
public static CellNameType getIndexComparator(CFMetaData baseMetadata, ColumnDefinition cfDef)
{
- if (cfDef.type.isCollection())
+ if (cfDef.type.isCollection() && cfDef.type.isMultiCell())
{
switch (((CollectionType)cfDef.type).kind)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java b/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java
index e69e656..a11a0d9 100644
--- a/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java
+++ b/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java
@@ -98,7 +98,7 @@ public class CompositesIndexOnCollectionValue extends CompositesIndex
@Override
public boolean supportsOperator(Operator operator)
{
- return operator == Operator.CONTAINS;
+ return operator == Operator.CONTAINS && !(columnDef.type instanceof SetType);
}
@Override
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/marshal/AbstractType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
index 4e1f2a3..8dd2ff3 100644
--- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java
+++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
@@ -223,6 +223,24 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>
return false;
}
+ public boolean isMultiCell()
+ {
+ return false;
+ }
+
+ public AbstractType<?> freeze()
+ {
+ return this;
+ }
+
+ /**
+ * @param ignoreFreezing if true, the type string will not be wrapped with FrozenType(...), even if this type is frozen.
+ */
+ public String toString(boolean ignoreFreezing)
+ {
+ return this.toString();
+ }
+
/**
* The number of subcomponents this type has.
* This is always 1, i.e. the type has only itself as "subcomponents", except for CompositeType.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/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 e63f2a5..24ad533 100644
--- a/src/java/org/apache/cassandra/db/marshal/CollectionType.java
+++ b/src/java/org/apache/cassandra/db/marshal/CollectionType.java
@@ -20,20 +20,20 @@ package org.apache.cassandra.db.marshal;
import java.nio.ByteBuffer;
import java.util.List;
+import org.apache.cassandra.db.Cell;
+import org.apache.cassandra.transport.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.cql3.CQL3Type;
-import org.apache.cassandra.db.Cell;
import org.apache.cassandra.serializers.CollectionSerializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.utils.ByteBufferUtil;
/**
- * The abstract validator that is the base for maps, sets and lists.
+ * The abstract validator that is the base for maps, sets and lists (both frozen and non-frozen).
*
* Please note that this comparator shouldn't be used "manually" (through thrift for instance).
- *
*/
public abstract class CollectionType<T> extends AbstractType<T>
{
@@ -56,27 +56,9 @@ public abstract class CollectionType<T> extends AbstractType<T>
public abstract AbstractType<?> nameComparator();
public abstract AbstractType<?> valueComparator();
- protected abstract void appendToStringBuilder(StringBuilder sb);
-
- public abstract List<ByteBuffer> serializedValues(List<Cell> cells);
-
@Override
public abstract CollectionSerializer<T> getSerializer();
- @Override
- public void validateCellValue(ByteBuffer cellValue) throws MarshalException
- {
- valueComparator().validate(cellValue);
- }
-
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- appendToStringBuilder(sb);
- return sb.toString();
- }
-
public String getString(ByteBuffer bytes)
{
return BytesType.instance.getString(bytes);
@@ -94,25 +76,18 @@ public abstract class CollectionType<T> extends AbstractType<T>
}
}
- @Override
- public boolean isCompatibleWith(AbstractType<?> previous)
+ public boolean isCollection()
{
- if (this == previous)
- return true;
-
- if (!getClass().equals(previous.getClass()))
- return false;
-
- CollectionType tprev = (CollectionType) previous;
- // The name is part of the Cell name, so we need sorting compatibility, i.e. isCompatibleWith().
- // But value is the Cell value, so isValueCompatibleWith() is enough
- return this.nameComparator().isCompatibleWith(tprev.nameComparator())
- && this.valueComparator().isValueCompatibleWith(tprev.valueComparator());
+ return true;
}
- public boolean isCollection()
+ @Override
+ public void validateCellValue(ByteBuffer cellValue) throws MarshalException
{
- return true;
+ if (isMultiCell())
+ valueComparator().validate(cellValue);
+ else
+ super.validateCellValue(cellValue);
}
/**
@@ -124,9 +99,11 @@ public abstract class CollectionType<T> extends AbstractType<T>
return kind == Kind.MAP;
}
- protected List<Cell> enforceLimit(List<Cell> cells, int version)
+ public List<Cell> enforceLimit(List<Cell> cells, int version)
{
- if (version >= 3 || cells.size() <= MAX_ELEMENTS)
+ assert isMultiCell();
+
+ if (version >= Server.VERSION_3 || cells.size() <= MAX_ELEMENTS)
return cells;
logger.error("Detected collection with {} elements, more than the {} limit. Only the first {} elements will be returned to the client. "
@@ -134,15 +111,75 @@ public abstract class CollectionType<T> extends AbstractType<T>
return cells.subList(0, MAX_ELEMENTS);
}
+ public abstract List<ByteBuffer> serializedValues(List<Cell> cells);
+
public ByteBuffer serializeForNativeProtocol(List<Cell> cells, int version)
{
+ assert isMultiCell();
cells = enforceLimit(cells, version);
List<ByteBuffer> values = serializedValues(cells);
return CollectionSerializer.pack(values, cells.size(), version);
}
+ @Override
+ public boolean isCompatibleWith(AbstractType<?> previous)
+ {
+ if (this == previous)
+ return true;
+
+ if (!getClass().equals(previous.getClass()))
+ return false;
+
+ CollectionType tprev = (CollectionType) previous;
+ if (this.isMultiCell() != tprev.isMultiCell())
+ return false;
+
+ // subclasses should handle compatibility checks for frozen collections
+ if (!this.isMultiCell())
+ return isCompatibleWithFrozen(tprev);
+
+ if (!this.nameComparator().isCompatibleWith(tprev.nameComparator()))
+ return false;
+
+ // the value comparator is only used for Cell values, so sorting doesn't matter
+ return this.valueComparator().isValueCompatibleWith(tprev.valueComparator());
+ }
+
+ @Override
+ public boolean isValueCompatibleWithInternal(AbstractType<?> previous)
+ {
+ // for multi-cell collections, compatibility and value-compatibility are the same
+ if (this.isMultiCell())
+ return isCompatibleWith(previous);
+
+ if (this == previous)
+ return true;
+
+ if (!getClass().equals(previous.getClass()))
+ return false;
+
+ CollectionType tprev = (CollectionType) previous;
+ if (this.isMultiCell() != tprev.isMultiCell())
+ return false;
+
+ // subclasses should handle compatibility checks for frozen collections
+ return isValueCompatibleWithFrozen(tprev);
+ }
+
+ /** A version of isCompatibleWith() to deal with non-multicell (frozen) collections */
+ protected abstract boolean isCompatibleWithFrozen(CollectionType<?> previous);
+
+ /** A version of isValueCompatibleWith() to deal with non-multicell (frozen) collections */
+ protected abstract boolean isValueCompatibleWithFrozen(CollectionType<?> previous);
+
public CQL3Type asCQL3Type()
{
return new CQL3Type.Collection(this);
}
+
+ @Override
+ public String toString()
+ {
+ return this.toString(false);
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java b/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java
index a28b874..6fb32fb 100644
--- a/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ColumnToCollectionType.java
@@ -33,7 +33,7 @@ import org.apache.cassandra.utils.ByteBufferUtil;
public class ColumnToCollectionType extends AbstractType<ByteBuffer>
{
// interning instances
- private static final Map<Map<ByteBuffer, CollectionType>, ColumnToCollectionType> instances = new HashMap<Map<ByteBuffer, CollectionType>, ColumnToCollectionType>();
+ private static final Map<Map<ByteBuffer, CollectionType>, ColumnToCollectionType> instances = new HashMap<>();
public final Map<ByteBuffer, CollectionType> defined;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/marshal/FrozenType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/FrozenType.java b/src/java/org/apache/cassandra/db/marshal/FrozenType.java
new file mode 100644
index 0000000..f440c90
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/marshal/FrozenType.java
@@ -0,0 +1,62 @@
+/*
+ * 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.List;
+
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.SyntaxException;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.serializers.MarshalException;
+
+/**
+ * A fake type that is only used for parsing type strings that include frozen types.
+ */
+public class FrozenType extends AbstractType<Void>
+{
+ public static AbstractType<?> getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
+ {
+ List<AbstractType<?>> innerTypes = parser.getTypeParameters();
+ if (innerTypes.size() != 1)
+ throw new SyntaxException("FrozenType() only accepts one parameter");
+
+ AbstractType<?> innerType = innerTypes.get(0);
+ return innerType.freeze();
+ }
+
+ public int compare(ByteBuffer o1, ByteBuffer o2)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getString(ByteBuffer bytes)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public ByteBuffer fromString(String source) throws MarshalException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public TypeSerializer<Void> getSerializer()
+ {
+ throw new UnsupportedOperationException();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/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 171e179..510a526 100644
--- a/src/java/org/apache/cassandra/db/marshal/ListType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ListType.java
@@ -24,17 +24,21 @@ import org.apache.cassandra.db.Cell;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.serializers.CollectionSerializer;
-import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.ListSerializer;
-import org.apache.cassandra.utils.ByteBufferUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class ListType<T> extends CollectionType<List<T>>
{
+ private static final Logger logger = LoggerFactory.getLogger(ListType.class);
+
// interning instances
- private static final Map<AbstractType<?>, ListType> instances = new HashMap<AbstractType<?>, ListType>();
+ private static final Map<AbstractType<?>, ListType> instances = new HashMap<>();
+ private static final Map<AbstractType<?>, ListType> frozenInstances = new HashMap<>();
- public final AbstractType<T> elements;
+ private final AbstractType<T> elements;
public final ListSerializer<T> serializer;
+ private final boolean isMultiCell;
public static ListType<?> getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
{
@@ -42,25 +46,32 @@ public class ListType<T> extends CollectionType<List<T>>
if (l.size() != 1)
throw new ConfigurationException("ListType takes exactly 1 type parameter");
- return getInstance(l.get(0));
+ return getInstance(l.get(0), true);
}
- public static synchronized <T> ListType<T> getInstance(AbstractType<T> elements)
+ public static synchronized <T> ListType<T> getInstance(AbstractType<T> elements, boolean isMultiCell)
{
- ListType<T> t = instances.get(elements);
+ Map<AbstractType<?>, ListType> internMap = isMultiCell ? instances : frozenInstances;
+ ListType<T> t = internMap.get(elements);
if (t == null)
{
- t = new ListType<T>(elements);
- instances.put(elements, t);
+ t = new ListType<T>(elements, isMultiCell);
+ internMap.put(elements, t);
}
return t;
}
- private ListType(AbstractType<T> elements)
+ private ListType(AbstractType<T> elements, boolean isMultiCell)
{
super(Kind.LIST);
this.elements = elements;
this.serializer = ListSerializer.getInstance(elements.getSerializer());
+ this.isMultiCell = isMultiCell;
+ }
+
+ public AbstractType<T> getElementsType()
+ {
+ return elements;
}
public AbstractType<UUID> nameComparator()
@@ -79,6 +90,35 @@ public class ListType<T> extends CollectionType<List<T>>
}
@Override
+ public AbstractType<?> freeze()
+ {
+ if (isMultiCell)
+ return getInstance(this.elements, false);
+ else
+ return this;
+ }
+
+ @Override
+ public boolean isMultiCell()
+ {
+ return isMultiCell;
+ }
+
+ @Override
+ public boolean isCompatibleWithFrozen(CollectionType<?> previous)
+ {
+ assert !isMultiCell;
+ return this.elements.isCompatibleWith(((ListType) previous).elements);
+ }
+
+ @Override
+ public boolean isValueCompatibleWithFrozen(CollectionType<?> previous)
+ {
+ assert !isMultiCell;
+ return this.elements.isValueCompatibleWithInternal(((ListType) previous).elements);
+ }
+
+ @Override
public int compare(ByteBuffer o1, ByteBuffer o2)
{
return compareListOrSet(elements, o1, o2);
@@ -86,7 +126,7 @@ public class ListType<T> extends CollectionType<List<T>>
static int compareListOrSet(AbstractType<?> elementsComparator, ByteBuffer o1, ByteBuffer o2)
{
- // Note that this is only used if the collection is inside an UDT
+ // Note that this is only used if the collection is frozen
if (!o1.hasRemaining() || !o2.hasRemaining())
return o1.hasRemaining() ? 1 : o2.hasRemaining() ? -1 : 0;
@@ -108,13 +148,24 @@ public class ListType<T> extends CollectionType<List<T>>
return size1 == size2 ? 0 : (size1 < size2 ? -1 : 1);
}
- protected void appendToStringBuilder(StringBuilder sb)
+ @Override
+ public String toString(boolean ignoreFreezing)
{
- sb.append(getClass().getName()).append(TypeParser.stringifyTypeParameters(Collections.<AbstractType<?>>singletonList(elements)));
+ boolean includeFrozenType = !ignoreFreezing && !isMultiCell();
+
+ StringBuilder sb = new StringBuilder();
+ if (includeFrozenType)
+ sb.append(FrozenType.class.getName()).append("(");
+ sb.append(getClass().getName());
+ sb.append(TypeParser.stringifyTypeParameters(Collections.<AbstractType<?>>singletonList(elements), ignoreFreezing || !isMultiCell));
+ if (includeFrozenType)
+ sb.append(")");
+ return sb.toString();
}
public List<ByteBuffer> serializedValues(List<Cell> cells)
{
+ assert isMultiCell;
List<ByteBuffer> bbs = new ArrayList<ByteBuffer>(cells.size());
for (Cell c : cells)
bbs.add(c.value());
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/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 dbf6721..64f3e2a 100644
--- a/src/java/org/apache/cassandra/db/marshal/MapType.java
+++ b/src/java/org/apache/cassandra/db/marshal/MapType.java
@@ -24,19 +24,20 @@ import org.apache.cassandra.db.Cell;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.serializers.CollectionSerializer;
-import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.MapSerializer;
+import org.apache.cassandra.transport.Server;
import org.apache.cassandra.utils.Pair;
-import org.apache.cassandra.utils.ByteBufferUtil;
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>();
+ private static final Map<Pair<AbstractType<?>, AbstractType<?>>, MapType> instances = new HashMap<>();
+ private static final Map<Pair<AbstractType<?>, AbstractType<?>>, MapType> frozenInstances = new HashMap<>();
- public final AbstractType<K> keys;
- public final AbstractType<V> values;
+ private final AbstractType<K> keys;
+ private final AbstractType<V> values;
private final MapSerializer<K, V> serializer;
+ private final boolean isMultiCell;
public static MapType<?, ?> getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
{
@@ -44,27 +45,39 @@ public class MapType<K, V> extends CollectionType<Map<K, V>>
if (l.size() != 2)
throw new ConfigurationException("MapType takes exactly 2 type parameters");
- return getInstance(l.get(0), l.get(1));
+ return getInstance(l.get(0), l.get(1), true);
}
- public static synchronized <K, V> MapType<K, V> getInstance(AbstractType<K> keys, AbstractType<V> values)
+ public static synchronized <K, V> MapType<K, V> getInstance(AbstractType<K> keys, AbstractType<V> values, boolean isMultiCell)
{
+ Map<Pair<AbstractType<?>, AbstractType<?>>, MapType> internMap = isMultiCell ? instances : frozenInstances;
Pair<AbstractType<?>, AbstractType<?>> p = Pair.<AbstractType<?>, AbstractType<?>>create(keys, values);
- MapType<K, V> t = instances.get(p);
+ MapType<K, V> t = internMap.get(p);
if (t == null)
{
- t = new MapType<K, V>(keys, values);
- instances.put(p, t);
+ t = new MapType<>(keys, values, isMultiCell);
+ internMap.put(p, t);
}
return t;
}
- private MapType(AbstractType<K> keys, AbstractType<V> values)
+ private MapType(AbstractType<K> keys, AbstractType<V> values, boolean isMultiCell)
{
super(Kind.MAP);
this.keys = keys;
this.values = values;
this.serializer = MapSerializer.getInstance(keys.getSerializer(), values.getSerializer());
+ this.isMultiCell = isMultiCell;
+ }
+
+ public AbstractType<K> getKeysType()
+ {
+ return keys;
+ }
+
+ public AbstractType<V> getValuesType()
+ {
+ return values;
}
public AbstractType<K> nameComparator()
@@ -78,29 +91,65 @@ public class MapType<K, V> extends CollectionType<Map<K, V>>
}
@Override
+ public boolean isMultiCell()
+ {
+ return isMultiCell;
+ }
+
+ @Override
+ public AbstractType<?> freeze()
+ {
+ if (isMultiCell)
+ return getInstance(this.keys, this.values, false);
+ else
+ return this;
+ }
+
+ @Override
+ public boolean isCompatibleWithFrozen(CollectionType<?> previous)
+ {
+ assert !isMultiCell;
+ MapType tprev = (MapType) previous;
+ return keys.isCompatibleWith(tprev.keys) && values.isCompatibleWith(tprev.values);
+ }
+
+ @Override
+ public boolean isValueCompatibleWithFrozen(CollectionType<?> previous)
+ {
+ assert !isMultiCell;
+ MapType tprev = (MapType) previous;
+ return keys.isCompatibleWith(tprev.keys) && values.isValueCompatibleWith(tprev.values);
+ }
+
+ @Override
public int compare(ByteBuffer o1, ByteBuffer o2)
{
- // Note that this is only used if the collection is inside an UDT
- if (!o1.hasRemaining() || !o2.hasRemaining())
+ return compareMaps(keys, values, o1, o2);
+ }
+
+ public static int compareMaps(AbstractType<?> keysComparator, AbstractType<?> valuesComparator, ByteBuffer o1, ByteBuffer o2)
+ {
+ if (!o1.hasRemaining() || !o2.hasRemaining())
return o1.hasRemaining() ? 1 : o2.hasRemaining() ? -1 : 0;
ByteBuffer bb1 = o1.duplicate();
ByteBuffer bb2 = o2.duplicate();
- int size1 = CollectionSerializer.readCollectionSize(bb1, 3);
- int size2 = CollectionSerializer.readCollectionSize(bb2, 3);
+ int protocolVersion = Server.VERSION_3;
+ int size1 = CollectionSerializer.readCollectionSize(bb1, protocolVersion);
+ int size2 = CollectionSerializer.readCollectionSize(bb2, protocolVersion);
for (int i = 0; i < Math.min(size1, size2); i++)
{
- ByteBuffer k1 = CollectionSerializer.readValue(bb1, 3);
- ByteBuffer k2 = CollectionSerializer.readValue(bb2, 3);
- int cmp = keys.compare(k1, k2);
+ ByteBuffer k1 = CollectionSerializer.readValue(bb1, protocolVersion);
+ ByteBuffer k2 = CollectionSerializer.readValue(bb2, protocolVersion);
+ int cmp = keysComparator.compare(k1, k2);
if (cmp != 0)
return cmp;
- ByteBuffer v1 = CollectionSerializer.readValue(bb1, 3);
- ByteBuffer v2 = CollectionSerializer.readValue(bb2, 3);
- cmp = values.compare(v1, v2);
+ ByteBuffer v1 = CollectionSerializer.readValue(bb1, protocolVersion);
+ ByteBuffer v2 = CollectionSerializer.readValue(bb2, protocolVersion);
+ cmp = valuesComparator.compare(v1, v2);
if (cmp != 0)
return cmp;
}
@@ -119,13 +168,23 @@ public class MapType<K, V> extends CollectionType<Map<K, V>>
return keys.isByteOrderComparable();
}
- protected void appendToStringBuilder(StringBuilder sb)
+ @Override
+ public String toString(boolean ignoreFreezing)
{
- sb.append(getClass().getName()).append(TypeParser.stringifyTypeParameters(Arrays.asList(keys, values)));
+ boolean includeFrozenType = !ignoreFreezing && !isMultiCell();
+
+ StringBuilder sb = new StringBuilder();
+ if (includeFrozenType)
+ sb.append(FrozenType.class.getName()).append("(");
+ sb.append(getClass().getName()).append(TypeParser.stringifyTypeParameters(Arrays.asList(keys, values), ignoreFreezing || !isMultiCell));
+ if (includeFrozenType)
+ sb.append(")");
+ return sb.toString();
}
public List<ByteBuffer> serializedValues(List<Cell> cells)
{
+ assert isMultiCell;
List<ByteBuffer> bbs = new ArrayList<ByteBuffer>(cells.size() * 2);
for (Cell c : cells)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/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 d2f7f12..e10f2a1 100644
--- a/src/java/org/apache/cassandra/db/marshal/SetType.java
+++ b/src/java/org/apache/cassandra/db/marshal/SetType.java
@@ -23,16 +23,17 @@ import java.util.*;
import org.apache.cassandra.db.Cell;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
-import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.SetSerializer;
public class SetType<T> extends CollectionType<Set<T>>
{
// interning instances
- private static final Map<AbstractType<?>, SetType> instances = new HashMap<AbstractType<?>, SetType>();
+ private static final Map<AbstractType<?>, SetType> instances = new HashMap<>();
+ private static final Map<AbstractType<?>, SetType> frozenInstances = new HashMap<>();
- public final AbstractType<T> elements;
+ private final AbstractType<T> elements;
private final SetSerializer<T> serializer;
+ private final boolean isMultiCell;
public static SetType<?> getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
{
@@ -40,25 +41,32 @@ public class SetType<T> extends CollectionType<Set<T>>
if (l.size() != 1)
throw new ConfigurationException("SetType takes exactly 1 type parameter");
- return getInstance(l.get(0));
+ return getInstance(l.get(0), true);
}
- public static synchronized <T> SetType<T> getInstance(AbstractType<T> elements)
+ public static synchronized <T> SetType<T> getInstance(AbstractType<T> elements, boolean isMultiCell)
{
- SetType<T> t = instances.get(elements);
+ Map<AbstractType<?>, SetType> internMap = isMultiCell ? instances : frozenInstances;
+ SetType<T> t = internMap.get(elements);
if (t == null)
{
- t = new SetType<T>(elements);
- instances.put(elements, t);
+ t = new SetType<T>(elements, isMultiCell);
+ internMap.put(elements, t);
}
return t;
}
- public SetType(AbstractType<T> elements)
+ public SetType(AbstractType<T> elements, boolean isMultiCell)
{
super(Kind.SET);
this.elements = elements;
this.serializer = SetSerializer.getInstance(elements.getSerializer());
+ this.isMultiCell = isMultiCell;
+ }
+
+ public AbstractType<T> getElementsType()
+ {
+ return elements;
}
public AbstractType<T> nameComparator()
@@ -72,6 +80,35 @@ public class SetType<T> extends CollectionType<Set<T>>
}
@Override
+ public boolean isMultiCell()
+ {
+ return isMultiCell;
+ }
+
+ @Override
+ public AbstractType<?> freeze()
+ {
+ if (isMultiCell)
+ return getInstance(this.elements, false);
+ else
+ return this;
+ }
+
+ @Override
+ public boolean isCompatibleWithFrozen(CollectionType<?> previous)
+ {
+ assert !isMultiCell;
+ return this.elements.isCompatibleWith(((SetType) previous).elements);
+ }
+
+ @Override
+ public boolean isValueCompatibleWithFrozen(CollectionType<?> previous)
+ {
+ // because sets are ordered, any changes to the type must maintain the ordering
+ return isCompatibleWithFrozen(previous);
+ }
+
+ @Override
public int compare(ByteBuffer o1, ByteBuffer o2)
{
return ListType.compareListOrSet(elements, o1, o2);
@@ -87,9 +124,19 @@ public class SetType<T> extends CollectionType<Set<T>>
return elements.isByteOrderComparable();
}
- protected void appendToStringBuilder(StringBuilder sb)
+ @Override
+ public String toString(boolean ignoreFreezing)
{
- sb.append(getClass().getName()).append(TypeParser.stringifyTypeParameters(Collections.<AbstractType<?>>singletonList(elements)));
+ boolean includeFrozenType = !ignoreFreezing && !isMultiCell();
+
+ StringBuilder sb = new StringBuilder();
+ if (includeFrozenType)
+ sb.append(FrozenType.class.getName()).append("(");
+ sb.append(getClass().getName());
+ sb.append(TypeParser.stringifyTypeParameters(Collections.<AbstractType<?>>singletonList(elements), ignoreFreezing || !isMultiCell));
+ if (includeFrozenType)
+ sb.append(")");
+ return sb.toString();
}
public List<ByteBuffer> serializedValues(List<Cell> cells)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TupleType.java b/src/java/org/apache/cassandra/db/marshal/TupleType.java
index 42aaba1..ddaf53f 100644
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@ -41,12 +41,17 @@ public class TupleType extends AbstractType<ByteBuffer>
public TupleType(List<AbstractType<?>> types)
{
+ for (int i = 0; i < types.size(); i++)
+ types.set(i, types.get(i).freeze());
this.types = types;
}
public static TupleType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
{
- return new TupleType(parser.getTypeParameters());
+ List<AbstractType<?>> types = parser.getTypeParameters();
+ for (int i = 0; i < types.size(); i++)
+ types.set(i, types.get(i).freeze());
+ return new TupleType(types);
}
public AbstractType<?> type(int i)
@@ -293,6 +298,6 @@ public class TupleType extends AbstractType<ByteBuffer>
@Override
public String toString()
{
- return getClass().getName() + TypeParser.stringifyTypeParameters(types);
+ return getClass().getName() + TypeParser.stringifyTypeParameters(types, true);
}
}
[3/4] cassandra git commit: Support for frozen collections
Posted by ty...@apache.org.
Support for frozen collections
Patch by Tyler Hobbs; reviewed by Sylvain Lebresne for CASSANDRA-7859
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/ee55f361
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/ee55f361
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/ee55f361
Branch: refs/heads/trunk
Commit: ee55f361b76f9ce7dd2a21a0ff4e80da931c77d2
Parents: 0337620
Author: Tyler Hobbs <ty...@datastax.com>
Authored: Tue Nov 11 12:40:48 2014 -0600
Committer: Tyler Hobbs <ty...@datastax.com>
Committed: Tue Nov 11 12:40:48 2014 -0600
----------------------------------------------------------------------
CHANGES.txt | 1 +
bin/cqlsh | 18 +
pylib/cqlshlib/cql3handling.py | 19 +-
.../apache/cassandra/cql3/AbstractMarker.java | 2 +-
src/java/org/apache/cassandra/cql3/CQL3Row.java | 2 +-
.../org/apache/cassandra/cql3/CQL3Type.java | 158 ++--
.../apache/cassandra/cql3/ColumnCondition.java | 275 ++++---
.../org/apache/cassandra/cql3/Constants.java | 3 +-
src/java/org/apache/cassandra/cql3/Cql.g | 14 +-
src/java/org/apache/cassandra/cql3/Lists.java | 66 +-
src/java/org/apache/cassandra/cql3/Maps.java | 62 +-
.../org/apache/cassandra/cql3/Operation.java | 16 +-
src/java/org/apache/cassandra/cql3/Sets.java | 80 +-
src/java/org/apache/cassandra/cql3/Term.java | 6 +
src/java/org/apache/cassandra/cql3/Tuples.java | 13 +-
.../apache/cassandra/cql3/UntypedResultSet.java | 6 +-
.../apache/cassandra/cql3/UpdateParameters.java | 2 +-
.../org/apache/cassandra/cql3/UserTypes.java | 3 +-
.../cql3/statements/AlterTableStatement.java | 39 +-
.../cql3/statements/AlterTypeStatement.java | 18 +-
.../cql3/statements/CreateIndexStatement.java | 24 +-
.../cql3/statements/CreateTableStatement.java | 28 +-
.../cql3/statements/DeleteStatement.java | 2 +-
.../cql3/statements/DropTypeStatement.java | 6 +-
.../cassandra/cql3/statements/IndexTarget.java | 23 +-
.../cassandra/cql3/statements/Restriction.java | 6 +
.../cql3/statements/SelectStatement.java | 107 ++-
.../statements/SingleColumnRestriction.java | 24 +
.../org/apache/cassandra/db/CFRowAdder.java | 4 +-
.../db/composites/AbstractCellNameType.java | 4 +-
.../cassandra/db/composites/CellNameType.java | 2 +-
.../composites/CompoundSparseCellNameType.java | 5 +-
.../cassandra/db/filter/ExtendedFilter.java | 47 +-
.../cassandra/db/index/SecondaryIndex.java | 6 +-
.../db/index/SecondaryIndexManager.java | 32 +
.../db/index/SecondaryIndexSearcher.java | 2 +
.../db/index/composites/CompositesIndex.java | 4 +-
.../CompositesIndexOnCollectionValue.java | 2 +-
.../cassandra/db/marshal/AbstractType.java | 18 +
.../cassandra/db/marshal/CollectionType.java | 113 ++-
.../db/marshal/ColumnToCollectionType.java | 2 +-
.../apache/cassandra/db/marshal/FrozenType.java | 62 ++
.../apache/cassandra/db/marshal/ListType.java | 77 +-
.../apache/cassandra/db/marshal/MapType.java | 105 ++-
.../apache/cassandra/db/marshal/SetType.java | 69 +-
.../apache/cassandra/db/marshal/TupleType.java | 9 +-
.../apache/cassandra/db/marshal/TypeParser.java | 34 +-
.../apache/cassandra/db/marshal/UserType.java | 2 +-
.../apache/cassandra/hadoop/pig/CqlStorage.java | 8 +-
.../serializers/CollectionSerializer.java | 24 +-
.../cassandra/serializers/ListSerializer.java | 36 +-
.../cassandra/serializers/MapSerializer.java | 38 +-
.../apache/cassandra/transport/DataType.java | 16 +-
.../org/apache/cassandra/cql3/CQLTester.java | 84 +-
.../cassandra/cql3/ColumnConditionTest.java | 28 +-
.../cassandra/cql3/FrozenCollectionsTest.java | 791 +++++++++++++++++++
.../apache/cassandra/cql3/TupleTypeTest.java | 44 +-
.../db/marshal/CollectionTypeTest.java | 22 +-
.../cassandra/transport/SerDeserTest.java | 13 +-
.../cassandra/stress/generate/values/Lists.java | 2 +-
.../cassandra/stress/generate/values/Sets.java | 2 +-
61 files changed, 2139 insertions(+), 591 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index b1b5df8..5b63f48 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.1.3
+ * Support for frozen collections (CASSANDRA-7859)
* Fix overflow on histogram computation (CASSANDRA-8028)
* Have paxos reuse the timestamp generation of normal queries (CASSANDRA-7801)
Merged from 2.0:
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/bin/cqlsh
----------------------------------------------------------------------
diff --git a/bin/cqlsh b/bin/cqlsh
index 6ace914..f105273 100755
--- a/bin/cqlsh
+++ b/bin/cqlsh
@@ -486,6 +486,24 @@ def auto_format_udts():
cassandra.cqltypes.UserType.make_udt_class = classmethod(new_make_udt_class)
+class FrozenType(cassandra.cqltypes._ParameterizedType):
+ """
+ Needed until the bundled python driver adds FrozenType.
+ """
+ typename = "frozen"
+ num_subtypes = 1
+
+ @classmethod
+ def deserialize_safe(cls, byts, protocol_version):
+ subtype, = cls.subtypes
+ return subtype.from_binary(byts)
+
+ @classmethod
+ def serialize_safe(cls, val, protocol_version):
+ subtype, = cls.subtypes
+ return subtype.to_binary(val, protocol_version)
+
+
class Shell(cmd.Cmd):
custom_prompt = os.getenv('CQLSH_PROMPT', '')
if custom_prompt is not '':
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/pylib/cqlshlib/cql3handling.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py
index b1179ca..dbd3709 100644
--- a/pylib/cqlshlib/cql3handling.py
+++ b/pylib/cqlshlib/cql3handling.py
@@ -255,13 +255,20 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
<userType> ::= utname=<cfOrKsName> ;
-<storageType> ::= <simpleStorageType> | <collectionType> | <userType> ;
+<storageType> ::= <simpleStorageType> | <collectionType> | <frozenCollectionType> | <userType> ;
+# Note: autocomplete for frozen collection types does not handle nesting past depth 1 properly,
+# but that's a lot of work to fix for little benefit.
<collectionType> ::= "map" "<" <simpleStorageType> "," ( <simpleStorageType> | <userType> ) ">"
| "list" "<" ( <simpleStorageType> | <userType> ) ">"
| "set" "<" ( <simpleStorageType> | <userType> ) ">"
;
+<frozenCollectionType> ::= "frozen" "<" "map" "<" <storageType> "," <storageType> ">" ">"
+ | "frozen" "<" "list" "<" <storageType> ">" ">"
+ | "frozen" "<" "set" "<" <storageType> ">" ">"
+ ;
+
<columnFamilyName> ::= ( ksname=<cfOrKsName> dot="." )? cfname=<cfOrKsName> ;
<userTypeName> ::= ( ksname=<cfOrKsName> dot="." )? utname=<cfOrKsName> ;
@@ -906,11 +913,11 @@ syntax_rules += r'''
<cfamOrdering> ::= [ordercol]=<cident> ( "ASC" | "DESC" )
;
-<singleKeyCfSpec> ::= [newcolname]=<cident> <simpleStorageType> "PRIMARY" "KEY"
+<singleKeyCfSpec> ::= [newcolname]=<cident> <storageType> "PRIMARY" "KEY"
( "," [newcolname]=<cident> <storageType> )*
;
-<compositeKeyCfSpec> ::= [newcolname]=<cident> <simpleStorageType>
+<compositeKeyCfSpec> ::= [newcolname]=<cident> <storageType>
"," [newcolname]=<cident> <storageType> ( "static" )?
( "," [newcolname]=<cident> <storageType> ( "static" )? )*
"," "PRIMARY" k="KEY" p="(" ( partkey=<pkDef> | [pkey]=<cident> )
@@ -986,7 +993,11 @@ def create_cf_composite_primary_key_comma_completer(ctxt, cass):
syntax_rules += r'''
<createIndexStatement> ::= "CREATE" "CUSTOM"? "INDEX" ("IF" "NOT" "EXISTS")? indexname=<identifier>? "ON"
- cf=<columnFamilyName> ( "(" col=<cident> ")" | "(" "KEYS" "(" col=<cident> ")" ")")
+ cf=<columnFamilyName> "(" (
+ col=<cident> |
+ "keys(" col=<cident> ")" |
+ "fullCollection(" col=<cident> ")"
+ ) ")"
( "USING" <stringLiteral> ( "WITH" "OPTIONS" "=" <mapLiteral> )? )?
;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/AbstractMarker.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/AbstractMarker.java b/src/java/org/apache/cassandra/cql3/AbstractMarker.java
index 10a4dff..d18790c 100644
--- a/src/java/org/apache/cassandra/cql3/AbstractMarker.java
+++ b/src/java/org/apache/cassandra/cql3/AbstractMarker.java
@@ -99,7 +99,7 @@ public abstract class AbstractMarker extends Term.NonTerminal
private static ColumnSpecification makeInReceiver(ColumnSpecification receiver)
{
ColumnIdentifier inName = new ColumnIdentifier("in(" + receiver.name + ")", true);
- return new ColumnSpecification(receiver.ksName, receiver.cfName, inName, ListType.getInstance(receiver.type));
+ return new ColumnSpecification(receiver.ksName, receiver.cfName, inName, ListType.getInstance(receiver.type, false));
}
@Override
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/CQL3Row.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/CQL3Row.java b/src/java/org/apache/cassandra/cql3/CQL3Row.java
index 6fa2b64..e3e76d1 100644
--- a/src/java/org/apache/cassandra/cql3/CQL3Row.java
+++ b/src/java/org/apache/cassandra/cql3/CQL3Row.java
@@ -27,7 +27,7 @@ public interface CQL3Row
{
public ByteBuffer getClusteringColumn(int i);
public Cell getColumn(ColumnIdentifier name);
- public List<Cell> getCollection(ColumnIdentifier name);
+ public List<Cell> getMultiCellColumn(ColumnIdentifier name);
public interface Builder
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/CQL3Type.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/CQL3Type.java b/src/java/org/apache/cassandra/cql3/CQL3Type.java
index 6d55285..b656de8 100644
--- a/src/java/org/apache/cassandra/cql3/CQL3Type.java
+++ b/src/java/org/apache/cassandra/cql3/CQL3Type.java
@@ -26,9 +26,13 @@ import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public interface CQL3Type
{
+ static final Logger logger = LoggerFactory.getLogger(CQL3Type.class);
+
public boolean isCollection();
public AbstractType<?> getType();
@@ -160,17 +164,30 @@ public interface CQL3Type
@Override
public String toString()
{
+ boolean isFrozen = !this.type.isMultiCell();
+ StringBuilder sb = new StringBuilder(isFrozen ? "frozen<" : "");
switch (type.kind)
{
case LIST:
- return "list<" + ((ListType)type).elements.asCQL3Type() + ">";
+ AbstractType<?> listType = ((ListType)type).getElementsType();
+ sb.append("list<").append(listType.asCQL3Type());
+ break;
case SET:
- return "set<" + ((SetType)type).elements.asCQL3Type() + ">";
+ AbstractType<?> setType = ((SetType)type).getElementsType();
+ sb.append("set<").append(setType.asCQL3Type());
+ break;
case MAP:
- MapType mt = (MapType)type;
- return "map<" + mt.keys.asCQL3Type() + ", " + mt.values.asCQL3Type() + ">";
+ AbstractType<?> keysType = ((MapType)type).getKeysType();
+ AbstractType<?> valuesType = ((MapType)type).getValuesType();
+ sb.append("map<").append(keysType.asCQL3Type()).append(", ").append(valuesType.asCQL3Type());
+ break;
+ default:
+ throw new AssertionError();
}
- throw new AssertionError();
+ sb.append(">");
+ if (isFrozen)
+ sb.append(">");
+ return sb.toString();
}
}
@@ -284,7 +301,9 @@ public interface CQL3Type
// actual type used, so Raw is a "not yet prepared" CQL3Type.
public abstract class Raw
{
- protected boolean frozen;
+ protected boolean frozen = false;
+
+ protected abstract boolean supportsFreezing();
public boolean isCollection()
{
@@ -296,10 +315,10 @@ public interface CQL3Type
return false;
}
- public Raw freeze()
+ public void freeze() throws InvalidRequestException
{
- frozen = true;
- return this;
+ String message = String.format("frozen<> is only allowed on collections, tuples, and user-defined types (got %s)", this);
+ throw new InvalidRequestException(message);
}
public abstract CQL3Type prepare(String keyspace) throws InvalidRequestException;
@@ -314,53 +333,30 @@ public interface CQL3Type
return new RawUT(name);
}
- public static Raw map(CQL3Type.Raw t1, CQL3Type.Raw t2) throws InvalidRequestException
+ public static Raw map(CQL3Type.Raw t1, CQL3Type.Raw t2)
{
- if (t1.isCollection() || t2.isCollection())
- throw new InvalidRequestException("map type cannot contain another collection");
- if (t1.isCounter() || t2.isCounter())
- throw new InvalidRequestException("counters are not allowed inside a collection");
-
return new RawCollection(CollectionType.Kind.MAP, t1, t2);
}
- public static Raw list(CQL3Type.Raw t) throws InvalidRequestException
+ public static Raw list(CQL3Type.Raw t)
{
- if (t.isCollection())
- throw new InvalidRequestException("list type cannot contain another collection");
- if (t.isCounter())
- throw new InvalidRequestException("counters are not allowed inside a collection");
-
return new RawCollection(CollectionType.Kind.LIST, null, t);
}
- public static Raw set(CQL3Type.Raw t) throws InvalidRequestException
+ public static Raw set(CQL3Type.Raw t)
{
- if (t.isCollection())
- throw new InvalidRequestException("set type cannot contain another collection");
- if (t.isCounter())
- throw new InvalidRequestException("counters are not allowed inside a collection");
-
return new RawCollection(CollectionType.Kind.SET, null, t);
}
- public static Raw tuple(List<CQL3Type.Raw> ts) throws InvalidRequestException
+ public static Raw tuple(List<CQL3Type.Raw> ts)
{
- for (int i = 0; i < ts.size(); i++)
- if (ts.get(i) != null && ts.get(i).isCounter())
- throw new InvalidRequestException("counters are not allowed inside tuples");
-
return new RawTuple(ts);
}
public static Raw frozen(CQL3Type.Raw t) throws InvalidRequestException
{
- if (t instanceof RawUT)
- return ((RawUT)t).freeze();
- if (t instanceof RawTuple)
- return ((RawTuple)t).freeze();
-
- throw new InvalidRequestException("frozen<> is only currently only allowed on User-Defined and tuple types");
+ t.freeze();
+ return t;
}
private static class RawType extends Raw
@@ -377,6 +373,11 @@ public interface CQL3Type
return type;
}
+ protected boolean supportsFreezing()
+ {
+ return false;
+ }
+
public boolean isCounter()
{
return type == Native.COUNTER;
@@ -402,12 +403,18 @@ public interface CQL3Type
this.values = values;
}
- public Raw freeze()
+ public void freeze() throws InvalidRequestException
{
- if (keys != null)
+ if (keys != null && keys.supportsFreezing())
keys.freeze();
- values.freeze();
- return super.freeze();
+ if (values != null && values.supportsFreezing())
+ values.freeze();
+ frozen = true;
+ }
+
+ protected boolean supportsFreezing()
+ {
+ return true;
}
public boolean isCollection()
@@ -417,11 +424,28 @@ public interface CQL3Type
public CQL3Type prepare(String keyspace) throws InvalidRequestException
{
+ assert values != null : "Got null values type for a collection";
+
+ if (!frozen && values.supportsFreezing() && !values.frozen)
+ throw new InvalidRequestException("Non-frozen collections are not allowed inside collections: " + this);
+ if (values.isCounter())
+ throw new InvalidRequestException("Counters are not allowed inside collections: " + this);
+
+ if (keys != null)
+ {
+ if (!frozen && keys.supportsFreezing() && !keys.frozen)
+ throw new InvalidRequestException("Non-frozen collections are not allowed inside collections: " + this);
+ }
+
switch (kind)
{
- case LIST: return new Collection(ListType.getInstance(values.prepare(keyspace).getType()));
- case SET: return new Collection(SetType.getInstance(values.prepare(keyspace).getType()));
- case MAP: return new Collection(MapType.getInstance(keys.prepare(keyspace).getType(), values.prepare(keyspace).getType()));
+ case LIST:
+ return new Collection(ListType.getInstance(values.prepare(keyspace).getType(), !frozen));
+ case SET:
+ return new Collection(SetType.getInstance(values.prepare(keyspace).getType(), !frozen));
+ case MAP:
+ assert keys != null : "Got null keys type for a collection";
+ return new Collection(MapType.getInstance(keys.prepare(keyspace).getType(), values.prepare(keyspace).getType(), !frozen));
}
throw new AssertionError();
}
@@ -429,11 +453,13 @@ public interface CQL3Type
@Override
public String toString()
{
+ String start = frozen? "frozen<" : "";
+ String end = frozen ? ">" : "";
switch (kind)
{
- case LIST: return "list<" + values + ">";
- case SET: return "set<" + values + ">";
- case MAP: return "map<" + keys + ", " + values + ">";
+ case LIST: return start + "list<" + values + ">" + end;
+ case SET: return start + "set<" + values + ">" + end;
+ case MAP: return start + "map<" + keys + ", " + values + ">" + end;
}
throw new AssertionError();
}
@@ -448,6 +474,11 @@ public interface CQL3Type
this.name = name;
}
+ public void freeze()
+ {
+ frozen = true;
+ }
+
public CQL3Type prepare(String keyspace) throws InvalidRequestException
{
if (name.hasKeyspace())
@@ -477,7 +508,7 @@ public interface CQL3Type
return new UserDefined(name.toString(), type);
}
- public boolean isUDT()
+ protected boolean supportsFreezing()
{
return true;
}
@@ -498,12 +529,9 @@ public interface CQL3Type
this.types = types;
}
- public Raw freeze()
+ protected boolean supportsFreezing()
{
- for (CQL3Type.Raw t : types)
- if (t != null)
- t.freeze();
- return super.freeze();
+ return true;
}
public boolean isCollection()
@@ -511,15 +539,29 @@ public interface CQL3Type
return false;
}
- public CQL3Type prepare(String keyspace) throws InvalidRequestException
+ public void freeze() throws InvalidRequestException
{
- List<AbstractType<?>> ts = new ArrayList<>(types.size());
for (CQL3Type.Raw t : types)
- ts.add(t.prepare(keyspace).getType());
+ {
+ if (t.supportsFreezing())
+ t.freeze();
+ }
+ frozen = true;
+ }
+ public CQL3Type prepare(String keyspace) throws InvalidRequestException
+ {
if (!frozen)
- throw new InvalidRequestException("Non-frozen tuples are not supported, please use frozen<>");
+ freeze();
+ List<AbstractType<?>> ts = new ArrayList<>(types.size());
+ for (CQL3Type.Raw t : types)
+ {
+ if (t.isCounter())
+ throw new InvalidRequestException("Counters are not allowed inside tuples");
+
+ ts.add(t.prepare(keyspace).getType());
+ }
return new Tuple(new TupleType(ts));
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/ColumnCondition.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/ColumnCondition.java b/src/java/org/apache/cassandra/cql3/ColumnCondition.java
index 25cb07d..1a8e5a3 100644
--- a/src/java/org/apache/cassandra/cql3/ColumnCondition.java
+++ b/src/java/org/apache/cassandra/cql3/ColumnCondition.java
@@ -20,7 +20,6 @@ package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
import java.util.*;
-import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import static com.google.common.collect.Lists.newArrayList;
@@ -33,12 +32,13 @@ import org.apache.cassandra.db.composites.Composite;
import org.apache.cassandra.db.filter.ColumnSlice;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.transport.Server;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * A CQL3 condition.
+ * A CQL3 condition on the value of a column or collection element. For example, "UPDATE .. IF a = 0".
*/
public class ColumnCondition
{
@@ -235,12 +235,6 @@ public class ColumnCondition
CellName name = current.metadata().comparator.create(rowPrefix, column);
return isSatisfiedByValue(value, current.getColumn(name), column.type, operator, now);
}
-
- @Override
- public int hashCode()
- {
- return Objects.hashCode(column, value, operator);
- }
}
/**
@@ -275,12 +269,6 @@ public class ColumnCondition
}
return false;
}
-
- @Override
- public int hashCode()
- {
- return Objects.hashCode(column, inValues, operator);
- }
}
/** A condition on an element of a collection column. IN operators are not supported here, see ElementAccessInBound. */
@@ -305,16 +293,37 @@ public class ColumnCondition
if (column.type instanceof MapType)
{
- Cell cell = current.getColumn(current.metadata().comparator.create(rowPrefix, column, collectionElement));
- return isSatisfiedByValue(value, cell, ((MapType) column.type).values, operator, now);
+ MapType mapType = (MapType) column.type;
+ if (column.type.isMultiCell())
+ {
+ Cell cell = current.getColumn(current.metadata().comparator.create(rowPrefix, column, collectionElement));
+ return isSatisfiedByValue(value, cell, mapType.getValuesType(), operator, now);
+ }
+ else
+ {
+ Cell cell = current.getColumn(current.metadata().comparator.create(rowPrefix, column));
+ ByteBuffer mapElementValue = cell.isLive(now) ? mapType.getSerializer().getSerializedValue(cell.value(), collectionElement, mapType.getKeysType())
+ : null;
+ return compareWithOperator(operator, mapType.getValuesType(), value, mapElementValue);
+ }
}
// sets don't have element access, so it's a list
- assert column.type instanceof ListType;
- ByteBuffer columnValue = getListItem(
- collectionColumns(current.metadata().comparator.create(rowPrefix, column), current, now),
- getListIndex(collectionElement));
- return compareWithOperator(operator, ((ListType)column.type).elements, value, columnValue);
+ ListType listType = (ListType) column.type;
+ if (column.type.isMultiCell())
+ {
+ ByteBuffer columnValue = getListItem(
+ collectionColumns(current.metadata().comparator.create(rowPrefix, column), current, now),
+ getListIndex(collectionElement));
+ return compareWithOperator(operator, listType.getElementsType(), value, columnValue);
+ }
+ else
+ {
+ Cell cell = current.getColumn(current.metadata().comparator.create(rowPrefix, column));
+ ByteBuffer listElementValue = cell.isLive(now) ? listType.getSerializer().getElement(cell.value(), getListIndex(collectionElement))
+ : null;
+ return compareWithOperator(operator, listType.getElementsType(), value, listElementValue);
+ }
}
static int getListIndex(ByteBuffer collectionElement) throws InvalidRequestException
@@ -338,12 +347,6 @@ public class ColumnCondition
{
return collectionElement;
}
-
- @Override
- public int hashCode()
- {
- return Objects.hashCode(column, collectionElement, value, operator);
- }
}
static class ElementAccessInBound extends Bound
@@ -375,47 +378,86 @@ public class ColumnCondition
CellNameType nameType = current.metadata().comparator;
if (column.type instanceof MapType)
{
- CellName name = nameType.create(rowPrefix, column, collectionElement);
- Cell item = current.getColumn(name);
- AbstractType<?> valueType = ((MapType) column.type).values;
- for (ByteBuffer value : inValues)
+ MapType mapType = (MapType) column.type;
+ AbstractType<?> valueType = mapType.getValuesType();
+ if (column.type.isMultiCell())
{
- if (isSatisfiedByValue(value, item, valueType, Operator.EQ, now))
- return true;
+ CellName name = nameType.create(rowPrefix, column, collectionElement);
+ Cell item = current.getColumn(name);
+ for (ByteBuffer value : inValues)
+ {
+ if (isSatisfiedByValue(value, item, valueType, Operator.EQ, now))
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ Cell cell = current.getColumn(nameType.create(rowPrefix, column));
+ ByteBuffer mapElementValue = null;
+ if (cell != null && cell.isLive(now))
+ mapElementValue = mapType.getSerializer().getSerializedValue(cell.value(), collectionElement, mapType.getKeysType());
+ for (ByteBuffer value : inValues)
+ {
+ if (value == null)
+ {
+ if (mapElementValue == null)
+ return true;
+ continue;
+ }
+ if (valueType.compare(value, mapElementValue) == 0)
+ return true;
+ }
+ return false;
}
- return false;
}
- assert column.type instanceof ListType;
- ByteBuffer columnValue = ElementAccessBound.getListItem(
- collectionColumns(nameType.create(rowPrefix, column), current, now),
- ElementAccessBound.getListIndex(collectionElement));
+ ListType listType = (ListType) column.type;
+ AbstractType<?> elementsType = listType.getElementsType();
+ if (column.type.isMultiCell())
+ {
+ ByteBuffer columnValue = ElementAccessBound.getListItem(
+ collectionColumns(nameType.create(rowPrefix, column), current, now),
+ ElementAccessBound.getListIndex(collectionElement));
- AbstractType<?> valueType = ((ListType) column.type).elements;
- for (ByteBuffer value : inValues)
+ for (ByteBuffer value : inValues)
+ {
+ if (compareWithOperator(Operator.EQ, elementsType, value, columnValue))
+ return true;
+ }
+ }
+ else
{
- if (compareWithOperator(Operator.EQ, valueType, value, columnValue))
- return true;
+ Cell cell = current.getColumn(nameType.create(rowPrefix, column));
+ ByteBuffer listElementValue = null;
+ if (cell != null && cell.isLive(now))
+ listElementValue = listType.getSerializer().getElement(cell.value(), ElementAccessBound.getListIndex(collectionElement));
+
+ for (ByteBuffer value : inValues)
+ {
+ if (value == null)
+ {
+ if (listElementValue == null)
+ return true;
+ continue;
+ }
+ if (elementsType.compare(value, listElementValue) == 0)
+ return true;
+ }
}
return false;
}
-
- @Override
- public int hashCode()
- {
- return Objects.hashCode(column, collectionElement, inValues, operator);
- }
}
/** A condition on an entire collection column. IN operators are not supported here, see CollectionInBound. */
static class CollectionBound extends Bound
{
- public final Term.Terminal value;
+ private final Term.Terminal value;
private CollectionBound(ColumnCondition condition, QueryOptions options) throws InvalidRequestException
{
super(condition.column, condition.operator);
- assert column.type instanceof CollectionType && condition.collectionElement == null;
+ assert column.type.isCollection() && condition.collectionElement == null;
assert !condition.operator.equals(Operator.IN);
this.value = condition.value.bind(options);
}
@@ -424,18 +466,44 @@ public class ColumnCondition
{
CollectionType type = (CollectionType)column.type;
- Iterator<Cell> iter = collectionColumns(current.metadata().comparator.create(rowPrefix, column), current, now);
+ if (type.isMultiCell())
+ {
+ Iterator<Cell> iter = collectionColumns(current.metadata().comparator.create(rowPrefix, column), current, now);
+ if (value == null)
+ {
+ if (operator.equals(Operator.EQ))
+ return !iter.hasNext();
+ else if (operator.equals(Operator.NEQ))
+ return iter.hasNext();
+ else
+ throw new InvalidRequestException(String.format("Invalid comparison with null for operator \"%s\"", operator));
+ }
+
+ return valueAppliesTo(type, iter, value, operator);
+ }
+
+ // frozen collections
+ Cell cell = current.getColumn(current.metadata().comparator.create(rowPrefix, column));
if (value == null)
{
- if (operator.equals(Operator.EQ))
- return !iter.hasNext();
- else if (operator.equals(Operator.NEQ))
- return iter.hasNext();
+ if (operator == Operator.EQ)
+ return cell == null || !cell.isLive(now);
+ else if (operator == Operator.NEQ)
+ return cell != null && cell.isLive(now);
else
throw new InvalidRequestException(String.format("Invalid comparison with null for operator \"%s\"", operator));
}
- return valueAppliesTo(type, iter, value, operator);
+ // make sure we use v3 serialization format for comparison
+ ByteBuffer conditionValue;
+ if (type.kind == CollectionType.Kind.LIST)
+ conditionValue = ((Lists.Value) value).getWithProtocolVersion(Server.VERSION_3);
+ else if (type.kind == CollectionType.Kind.SET)
+ conditionValue = ((Sets.Value) value).getWithProtocolVersion(Server.VERSION_3);
+ else
+ conditionValue = ((Maps.Value) value).getWithProtocolVersion(Server.VERSION_3);
+
+ return compareWithOperator(operator, type, conditionValue, cell.value());
}
static boolean valueAppliesTo(CollectionType type, Iterator<Cell> iter, Term.Terminal value, Operator operator)
@@ -495,15 +563,15 @@ public class ColumnCondition
static boolean listAppliesTo(ListType type, Iterator<Cell> iter, List<ByteBuffer> elements, Operator operator)
{
- return setOrListAppliesTo(type.elements, iter, elements.iterator(), operator, false);
+ return setOrListAppliesTo(type.getElementsType(), iter, elements.iterator(), operator, false);
}
static boolean setAppliesTo(SetType type, Iterator<Cell> iter, Set<ByteBuffer> elements, Operator operator)
{
ArrayList<ByteBuffer> sortedElements = new ArrayList<>(elements.size());
sortedElements.addAll(elements);
- Collections.sort(sortedElements, type.elements);
- return setOrListAppliesTo(type.elements, iter, sortedElements.iterator(), operator, true);
+ Collections.sort(sortedElements, type.getElementsType());
+ return setOrListAppliesTo(type.getElementsType(), iter, sortedElements.iterator(), operator, true);
}
static boolean mapAppliesTo(MapType type, Iterator<Cell> iter, Map<ByteBuffer, ByteBuffer> elements, Operator operator)
@@ -518,12 +586,12 @@ public class ColumnCondition
Cell c = iter.next();
// compare the keys
- int comparison = type.keys.compare(c.name().collectionElement(), conditionEntry.getKey());
+ int comparison = type.getKeysType().compare(c.name().collectionElement(), conditionEntry.getKey());
if (comparison != 0)
return evaluateComparisonWithOperator(comparison, operator);
// compare the values
- comparison = type.values.compare(c.value(), conditionEntry.getValue());
+ comparison = type.getValuesType().compare(c.value(), conditionEntry.getValue());
if (comparison != 0)
return evaluateComparisonWithOperator(comparison, operator);
}
@@ -534,33 +602,11 @@ public class ColumnCondition
// they're equal
return operator == Operator.EQ || operator == Operator.LTE || operator == Operator.GTE;
}
-
- @Override
- public int hashCode()
- {
- Object val = null;
- if (value != null)
- {
- switch (((CollectionType)column.type).kind)
- {
- case LIST:
- val = ((Lists.Value)value).elements.hashCode();
- break;
- case SET:
- val = ((Sets.Value)value).elements.hashCode();
- break;
- case MAP:
- val = ((Maps.Value)value).map.hashCode();
- break;
- }
- }
- return Objects.hashCode(column, val);
- }
}
public static class CollectionInBound extends Bound
{
- public final List<Term.Terminal> inValues;
+ private final List<Term.Terminal> inValues;
private CollectionInBound(ColumnCondition condition, QueryOptions options) throws InvalidRequestException
{
@@ -575,7 +621,7 @@ public class ColumnCondition
Lists.Marker inValuesMarker = (Lists.Marker) condition.value;
if (column.type instanceof ListType)
{
- ListType deserializer = ListType.getInstance(collectionType.valueComparator());
+ ListType deserializer = ListType.getInstance(collectionType.valueComparator(), false);
for (ByteBuffer buffer : inValuesMarker.bind(options).elements)
{
if (buffer == null)
@@ -586,7 +632,7 @@ public class ColumnCondition
}
else if (column.type instanceof MapType)
{
- MapType deserializer = MapType.getInstance(collectionType.nameComparator(), collectionType.valueComparator());
+ MapType deserializer = MapType.getInstance(collectionType.nameComparator(), collectionType.valueComparator(), false);
for (ByteBuffer buffer : inValuesMarker.bind(options).elements)
{
if (buffer == null)
@@ -597,7 +643,7 @@ public class ColumnCondition
}
else if (column.type instanceof SetType)
{
- SetType deserializer = SetType.getInstance(collectionType.valueComparator());
+ SetType deserializer = SetType.getInstance(collectionType.valueComparator(), false);
for (ByteBuffer buffer : inValuesMarker.bind(options).elements)
{
if (buffer == null)
@@ -618,45 +664,34 @@ public class ColumnCondition
{
CollectionType type = (CollectionType)column.type;
CellName name = current.metadata().comparator.create(rowPrefix, column);
-
- // copy iterator contents so that we can properly reuse them for each comparison with an IN value
- List<Cell> cells = newArrayList(collectionColumns(name, current, now));
- for (Term.Terminal value : inValues)
+ if (type.isMultiCell())
{
- if (CollectionBound.valueAppliesTo(type, cells.iterator(), value, Operator.EQ))
- return true;
+ // copy iterator contents so that we can properly reuse them for each comparison with an IN value
+ List<Cell> cells = newArrayList(collectionColumns(name, current, now));
+ for (Term.Terminal value : inValues)
+ {
+ if (CollectionBound.valueAppliesTo(type, cells.iterator(), value, Operator.EQ))
+ return true;
+ }
+ return false;
}
- return false;
- }
-
- @Override
- public int hashCode()
- {
- List<Collection<ByteBuffer>> inValueBuffers = new ArrayList<>(inValues.size());
- switch (((CollectionType)column.type).kind)
+ else
{
- case LIST:
- for (Term.Terminal term : inValues)
- inValueBuffers.add(term == null ? null : ((Lists.Value)term).elements);
- break;
- case SET:
- for (Term.Terminal term : inValues)
- inValueBuffers.add(term == null ? null : ((Sets.Value)term).elements);
- break;
- case MAP:
- for (Term.Terminal term : inValues)
+ Cell cell = current.getColumn(name);
+ for (Term.Terminal value : inValues)
+ {
+ if (value == null)
{
- if (term != null)
- {
- inValueBuffers.add(((Maps.Value)term).map.keySet());
- inValueBuffers.add(((Maps.Value)term).map.values());
- }
- else
- inValueBuffers.add(null);
+ if (cell == null || !cell.isLive(now))
+ return true;
}
- break;
+ else if (type.compare(((Term.CollectionTerminal)value).getWithProtocolVersion(Server.VERSION_3), cell.value()) == 0)
+ {
+ return true;
+ }
+ }
+ return false;
}
- return Objects.hashCode(column, inValueBuffers, operator);
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/Constants.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Constants.java b/src/java/org/apache/cassandra/cql3/Constants.java
index a8f0120..01fbdf0 100644
--- a/src/java/org/apache/cassandra/cql3/Constants.java
+++ b/src/java/org/apache/cassandra/cql3/Constants.java
@@ -18,7 +18,6 @@
package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
-import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -368,7 +367,7 @@ public abstract class Constants
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
CellName cname = cf.getComparator().create(prefix, column);
- if (column.type.isCollection())
+ if (column.type.isMultiCell())
cf.addAtom(params.makeRangeTombstone(cname.slice()));
else
cf.addColumn(params.makeTombstone(cname));
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g
index b1c598b..06ba81c 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -575,8 +575,9 @@ createIndexStatement returns [CreateIndexStatement expr]
;
indexIdent returns [IndexTarget.Raw id]
- : c=cident { $id = IndexTarget.Raw.of(c); }
+ : c=cident { $id = IndexTarget.Raw.valuesOf(c); }
| K_KEYS '(' c=cident ')' { $id = IndexTarget.Raw.keysOf(c); }
+ | K_FULL '(' c=cident ')' { $id = IndexTarget.Raw.fullCollection(c); }
;
@@ -1148,21 +1149,21 @@ native_type returns [CQL3Type t]
collection_type returns [CQL3Type.Raw pt]
: K_MAP '<' t1=comparatorType ',' t2=comparatorType '>'
- { try {
+ {
// if we can't parse either t1 or t2, antlr will "recover" and we may have t1 or t2 null.
if (t1 != null && t2 != null)
$pt = CQL3Type.Raw.map(t1, t2);
- } catch (InvalidRequestException e) { addRecognitionError(e.getMessage()); } }
+ }
| K_LIST '<' t=comparatorType '>'
- { try { if (t != null) $pt = CQL3Type.Raw.list(t); } catch (InvalidRequestException e) { addRecognitionError(e.getMessage()); } }
+ { if (t != null) $pt = CQL3Type.Raw.list(t); }
| K_SET '<' t=comparatorType '>'
- { try { if (t != null) $pt = CQL3Type.Raw.set(t); } catch (InvalidRequestException e) { addRecognitionError(e.getMessage()); } }
+ { if (t != null) $pt = CQL3Type.Raw.set(t); }
;
tuple_type returns [CQL3Type.Raw t]
: K_TUPLE '<' { List<CQL3Type.Raw> types = new ArrayList<>(); }
t1=comparatorType { types.add(t1); } (',' tn=comparatorType { types.add(tn); })*
- '>' { try { $t = CQL3Type.Raw.tuple(types); } catch (InvalidRequestException e) { addRecognitionError(e.getMessage()); }}
+ '>' { $t = CQL3Type.Raw.tuple(types); }
;
username
@@ -1227,6 +1228,7 @@ K_WHERE: W H E R E;
K_AND: A N D;
K_KEY: K E Y;
K_KEYS: K E Y S;
+K_FULL: F U L L;
K_INSERT: I N S E R T;
K_UPDATE: U P D A T E;
K_WITH: W I T H;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/Lists.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Lists.java b/src/java/org/apache/cassandra/cql3/Lists.java
index 9d22364..d5174d1 100644
--- a/src/java/org/apache/cassandra/cql3/Lists.java
+++ b/src/java/org/apache/cassandra/cql3/Lists.java
@@ -19,8 +19,6 @@ package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@@ -34,6 +32,7 @@ import org.apache.cassandra.db.marshal.ListType;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.serializers.CollectionSerializer;
import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.transport.Server;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.UUIDGen;
@@ -56,7 +55,7 @@ public abstract class Lists
public static ColumnSpecification valueSpecOf(ColumnSpecification column)
{
- return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((ListType)column.type).elements);
+ return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((ListType)column.type).getElementsType());
}
public static class Literal implements Term.Raw
@@ -124,7 +123,7 @@ public abstract class Lists
}
}
- public static class Value extends Term.MultiItemTerminal
+ public static class Value extends Term.MultiItemTerminal implements Term.CollectionTerminal
{
public final List<ByteBuffer> elements;
@@ -143,7 +142,7 @@ public abstract class Lists
List<ByteBuffer> elements = new ArrayList<ByteBuffer>(l.size());
for (Object element : l)
// elements can be null in lists that represent a set of IN values
- elements.add(element == null ? null : type.elements.decompose(element));
+ elements.add(element == null ? null : type.getElementsType().decompose(element));
return new Value(elements);
}
catch (MarshalException e)
@@ -154,7 +153,12 @@ public abstract class Lists
public ByteBuffer get(QueryOptions options)
{
- return CollectionSerializer.pack(elements, elements.size(), options.getProtocolVersion());
+ return getWithProtocolVersion(options.getProtocolVersion());
+ }
+
+ public ByteBuffer getWithProtocolVersion(int protocolVersion)
+ {
+ return CollectionSerializer.pack(elements, elements.size(), protocolVersion);
}
public boolean equals(ListType lt, Value v)
@@ -163,7 +167,7 @@ public abstract class Lists
return false;
for (int i = 0; i < elements.size(); i++)
- if (lt.elements.compare(elements.get(i), v.elements.get(i)) != 0)
+ if (lt.getElementsType().compare(elements.get(i), v.elements.get(i)) != 0)
return false;
return true;
@@ -292,9 +296,12 @@ public abstract class Lists
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
- // delete + append
- CellName name = cf.getComparator().create(prefix, column);
- cf.addAtom(params.makeTombstoneForOverwrite(name.slice()));
+ if (column.type.isMultiCell())
+ {
+ // delete + append
+ CellName name = cf.getComparator().create(prefix, column);
+ cf.addAtom(params.makeTombstoneForOverwrite(name.slice()));
+ }
Appender.doAppend(t, cf, prefix, column, params);
}
}
@@ -324,6 +331,9 @@ public abstract class Lists
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ // we should not get here for frozen lists
+ assert column.type.isMultiCell() : "Attempted to set an individual element on a frozen list";
+
ByteBuffer index = idx.bindAndGet(params.options);
ByteBuffer value = t.bindAndGet(params.options);
@@ -362,23 +372,36 @@ public abstract class Lists
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ assert column.type.isMultiCell() : "Attempted to append to a frozen list";
doAppend(t, cf, prefix, column, params);
}
static void doAppend(Term t, ColumnFamily cf, Composite prefix, ColumnDefinition column, UpdateParameters params) throws InvalidRequestException
{
Term.Terminal value = t.bind(params.options);
- // If we append null, do nothing. Note that for Setter, we've
- // already removed the previous value so we're good here too
- if (value == null)
- return;
-
- assert value instanceof Lists.Value;
- List<ByteBuffer> toAdd = ((Lists.Value)value).elements;
- for (int i = 0; i < toAdd.size(); i++)
+ Lists.Value listValue = (Lists.Value)value;
+ if (column.type.isMultiCell())
{
- ByteBuffer uuid = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes());
- cf.addColumn(params.makeColumn(cf.getComparator().create(prefix, column, uuid), toAdd.get(i)));
+ // If we append null, do nothing. Note that for Setter, we've
+ // already removed the previous value so we're good here too
+ if (value == null)
+ return;
+
+ List<ByteBuffer> toAdd = listValue.elements;
+ for (int i = 0; i < toAdd.size(); i++)
+ {
+ ByteBuffer uuid = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes());
+ cf.addColumn(params.makeColumn(cf.getComparator().create(prefix, column, uuid), toAdd.get(i)));
+ }
+ }
+ else
+ {
+ // for frozen lists, we're overwriting the whole cell value
+ CellName name = cf.getComparator().create(prefix, column);
+ if (value == null)
+ cf.addAtom(params.makeTombstone(name));
+ else
+ cf.addColumn(params.makeColumn(name, listValue.getWithProtocolVersion(Server.CURRENT_VERSION)));
}
}
}
@@ -392,6 +415,7 @@ public abstract class Lists
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ assert column.type.isMultiCell() : "Attempted to prepend to a frozen list";
Term.Terminal value = t.bind(params.options);
if (value == null)
return;
@@ -424,6 +448,7 @@ public abstract class Lists
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ assert column.type.isMultiCell() : "Attempted to delete from a frozen list";
List<Cell> existingList = params.getPrefetchedList(rowKey, column.name);
// We want to call bind before possibly returning to reject queries where the value provided is not a list.
Term.Terminal value = t.bind(params.options);
@@ -464,6 +489,7 @@ public abstract class Lists
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ assert column.type.isMultiCell() : "Attempted to delete an item by index from a frozen list";
Term.Terminal index = t.bind(params.options);
if (index == null)
throw new InvalidRequestException("Invalid null value for list index");
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/Maps.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Maps.java b/src/java/org/apache/cassandra/cql3/Maps.java
index ce0ba2a..5b58833 100644
--- a/src/java/org/apache/cassandra/cql3/Maps.java
+++ b/src/java/org/apache/cassandra/cql3/Maps.java
@@ -19,7 +19,6 @@ package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
@@ -36,6 +35,7 @@ import org.apache.cassandra.db.marshal.MapType;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.serializers.CollectionSerializer;
import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.transport.Server;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
@@ -48,12 +48,12 @@ public abstract class Maps
public static ColumnSpecification keySpecOf(ColumnSpecification column)
{
- return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("key(" + column.name + ")", true), ((MapType)column.type).keys);
+ return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("key(" + column.name + ")", true), ((MapType)column.type).getKeysType());
}
public static ColumnSpecification valueSpecOf(ColumnSpecification column)
{
- return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((MapType)column.type).values);
+ return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((MapType)column.type).getValuesType());
}
public static class Literal implements Term.Raw
@@ -86,7 +86,7 @@ public abstract class Maps
values.put(k, v);
}
- DelayedValue value = new DelayedValue(((MapType)receiver.type).keys, values);
+ DelayedValue value = new DelayedValue(((MapType)receiver.type).getKeysType(), values);
return allTerminal ? value.bind(QueryOptions.DEFAULT) : value;
}
@@ -134,7 +134,7 @@ public abstract class Maps
}
}
- public static class Value extends Term.Terminal
+ public static class Value extends Term.Terminal implements Term.CollectionTerminal
{
public final Map<ByteBuffer, ByteBuffer> map;
@@ -152,7 +152,7 @@ public abstract class Maps
Map<?, ?> m = (Map<?, ?>)type.getSerializer().deserializeForNativeProtocol(value, version);
Map<ByteBuffer, ByteBuffer> map = new LinkedHashMap<ByteBuffer, ByteBuffer>(m.size());
for (Map.Entry<?, ?> entry : m.entrySet())
- map.put(type.keys.decompose(entry.getKey()), type.values.decompose(entry.getValue()));
+ map.put(type.getKeysType().decompose(entry.getKey()), type.getValuesType().decompose(entry.getValue()));
return new Value(map);
}
catch (MarshalException e)
@@ -163,13 +163,18 @@ public abstract class Maps
public ByteBuffer get(QueryOptions options)
{
- List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(2 * map.size());
+ return getWithProtocolVersion(options.getProtocolVersion());
+ }
+
+ public ByteBuffer getWithProtocolVersion(int protocolVersion)
+ {
+ List<ByteBuffer> buffers = new ArrayList<>(2 * map.size());
for (Map.Entry<ByteBuffer, ByteBuffer> entry : map.entrySet())
{
buffers.add(entry.getKey());
buffers.add(entry.getValue());
}
- return CollectionSerializer.pack(buffers, map.size(), options.getProtocolVersion());
+ return CollectionSerializer.pack(buffers, map.size(), protocolVersion);
}
public boolean equals(MapType mt, Value v)
@@ -184,7 +189,7 @@ public abstract class Maps
{
Map.Entry<ByteBuffer, ByteBuffer> thisEntry = thisIter.next();
Map.Entry<ByteBuffer, ByteBuffer> thatEntry = thatIter.next();
- if (mt.keys.compare(thisEntry.getKey(), thatEntry.getKey()) != 0 || mt.values.compare(thisEntry.getValue(), thatEntry.getValue()) != 0)
+ if (mt.getKeysType().compare(thisEntry.getKey(), thatEntry.getKey()) != 0 || mt.getValuesType().compare(thisEntry.getValue(), thatEntry.getValue()) != 0)
return false;
}
@@ -266,9 +271,12 @@ public abstract class Maps
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
- // delete + put
- CellName name = cf.getComparator().create(prefix, column);
- cf.addAtom(params.makeTombstoneForOverwrite(name.slice()));
+ if (column.type.isMultiCell())
+ {
+ // delete + put
+ CellName name = cf.getComparator().create(prefix, column);
+ cf.addAtom(params.makeTombstoneForOverwrite(name.slice()));
+ }
Putter.doPut(t, cf, prefix, column, params);
}
}
@@ -292,6 +300,7 @@ public abstract class Maps
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ assert column.type.isMultiCell() : "Attempted to set a value for a single key on a frozen map";
ByteBuffer key = k.bindAndGet(params.options);
ByteBuffer value = t.bindAndGet(params.options);
if (key == null)
@@ -325,21 +334,33 @@ public abstract class Maps
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ assert column.type.isMultiCell() : "Attempted to add items to a frozen map";
doPut(t, cf, prefix, column, params);
}
static void doPut(Term t, ColumnFamily cf, Composite prefix, ColumnDefinition column, UpdateParameters params) throws InvalidRequestException
{
Term.Terminal value = t.bind(params.options);
- if (value == null)
- return;
- assert value instanceof Maps.Value;
-
- Map<ByteBuffer, ByteBuffer> toAdd = ((Maps.Value)value).map;
- for (Map.Entry<ByteBuffer, ByteBuffer> entry : toAdd.entrySet())
+ Maps.Value mapValue = (Maps.Value) value;
+ if (column.type.isMultiCell())
+ {
+ if (value == null)
+ return;
+
+ for (Map.Entry<ByteBuffer, ByteBuffer> entry : mapValue.map.entrySet())
+ {
+ CellName cellName = cf.getComparator().create(prefix, column, entry.getKey());
+ cf.addColumn(params.makeColumn(cellName, entry.getValue()));
+ }
+ }
+ else
{
- CellName cellName = cf.getComparator().create(prefix, column, entry.getKey());
- cf.addColumn(params.makeColumn(cellName, entry.getValue()));
+ // for frozen maps, we're overwriting the whole cell
+ CellName cellName = cf.getComparator().create(prefix, column);
+ if (value == null)
+ cf.addAtom(params.makeTombstone(cellName));
+ else
+ cf.addColumn(params.makeColumn(cellName, mapValue.getWithProtocolVersion(Server.CURRENT_VERSION)));
}
}
}
@@ -353,6 +374,7 @@ public abstract class Maps
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ assert column.type.isMultiCell() : "Attempted to delete a single key in a frozen map";
Term.Terminal key = t.bind(params.options);
if (key == null)
throw new InvalidRequestException("Invalid null map key");
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/Operation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Operation.java b/src/java/org/apache/cassandra/cql3/Operation.java
index 32b6a12..816acb2 100644
--- a/src/java/org/apache/cassandra/cql3/Operation.java
+++ b/src/java/org/apache/cassandra/cql3/Operation.java
@@ -163,7 +163,7 @@ public abstract class Operation
if (receiver.type instanceof CounterColumnType)
throw new InvalidRequestException(String.format("Cannot set the value of counter column %s (counters can only be incremented/decremented, not set)", receiver.name));
- if (!(receiver.type instanceof CollectionType))
+ if (!(receiver.type.isCollection()))
return new Constants.Setter(receiver, v);
switch (((CollectionType)receiver.type).kind)
@@ -206,6 +206,8 @@ public abstract class Operation
{
if (!(receiver.type instanceof CollectionType))
throw new InvalidRequestException(String.format("Invalid operation (%s) for non collection column %s", toString(receiver), receiver.name));
+ else if (!(receiver.type.isMultiCell()))
+ throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
switch (((CollectionType)receiver.type).kind)
{
@@ -255,6 +257,8 @@ public abstract class Operation
throw new InvalidRequestException(String.format("Invalid operation (%s) for non counter column %s", toString(receiver), receiver.name));
return new Constants.Adder(receiver, v);
}
+ else if (!(receiver.type.isMultiCell()))
+ throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
switch (((CollectionType)receiver.type).kind)
{
@@ -296,6 +300,8 @@ public abstract class Operation
throw new InvalidRequestException(String.format("Invalid operation (%s) for non counter column %s", toString(receiver), receiver.name));
return new Constants.Substracter(receiver, value.prepare(keyspace, receiver));
}
+ else if (!(receiver.type.isMultiCell()))
+ throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
switch (((CollectionType)receiver.type).kind)
{
@@ -308,7 +314,7 @@ public abstract class Operation
ColumnSpecification vr = new ColumnSpecification(receiver.ksName,
receiver.cfName,
receiver.name,
- SetType.getInstance(((MapType)receiver.type).keys));
+ SetType.getInstance(((MapType)receiver.type).getKeysType(), false));
return new Sets.Discarder(receiver, value.prepare(keyspace, vr));
}
throw new AssertionError();
@@ -340,6 +346,8 @@ public abstract class Operation
if (!(receiver.type instanceof ListType))
throw new InvalidRequestException(String.format("Invalid operation (%s) for non list column %s", toString(receiver), receiver.name));
+ else if (!(receiver.type.isMultiCell()))
+ throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen list column %s", toString(receiver), receiver.name));
return new Lists.Prepender(receiver, v);
}
@@ -394,8 +402,10 @@ public abstract class Operation
public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
{
- if (!(receiver.type instanceof CollectionType))
+ if (!(receiver.type.isCollection()))
throw new InvalidRequestException(String.format("Invalid deletion operation for non collection column %s", receiver.name));
+ else if (!(receiver.type.isMultiCell()))
+ throw new InvalidRequestException(String.format("Invalid deletion operation for frozen collection column %s", receiver.name));
switch (((CollectionType)receiver.type).kind)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/Sets.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Sets.java b/src/java/org/apache/cassandra/cql3/Sets.java
index 315d7d3..c5a8e05 100644
--- a/src/java/org/apache/cassandra/cql3/Sets.java
+++ b/src/java/org/apache/cassandra/cql3/Sets.java
@@ -18,15 +18,7 @@
package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.*;
import com.google.common.base.Joiner;
@@ -34,11 +26,13 @@ import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.composites.CellName;
import org.apache.cassandra.db.composites.Composite;
+import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.MapType;
import org.apache.cassandra.db.marshal.SetType;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.serializers.CollectionSerializer;
import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.transport.Server;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
@@ -51,7 +45,7 @@ public abstract class Sets
public static ColumnSpecification valueSpecOf(ColumnSpecification column)
{
- return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((SetType)column.type).elements);
+ return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((SetType)column.type).getElementsType());
}
public static class Literal implements Term.Raw
@@ -87,7 +81,7 @@ public abstract class Sets
values.add(t);
}
- DelayedValue value = new DelayedValue(((SetType)receiver.type).elements, values);
+ DelayedValue value = new DelayedValue(((SetType)receiver.type).getElementsType(), values);
return allTerminal ? value.bind(QueryOptions.DEFAULT) : value;
}
@@ -97,7 +91,7 @@ public abstract class Sets
{
// We've parsed empty maps as a set literal to break the ambiguity so
// handle that case now
- if (receiver.type instanceof MapType && elements.isEmpty())
+ if ((receiver.type instanceof MapType) && elements.isEmpty())
return;
throw new InvalidRequestException(String.format("Invalid set literal for %s of type %s", receiver.name, receiver.type.asCQL3Type()));
@@ -131,11 +125,11 @@ public abstract class Sets
}
}
- public static class Value extends Term.Terminal
+ public static class Value extends Term.Terminal implements Term.CollectionTerminal
{
- public final Set<ByteBuffer> elements;
+ public final SortedSet<ByteBuffer> elements;
- public Value(Set<ByteBuffer> elements)
+ public Value(SortedSet<ByteBuffer> elements)
{
this.elements = elements;
}
@@ -147,9 +141,9 @@ public abstract class Sets
// Collections have this small hack that validate cannot be called on a serialized object,
// but compose does the validation (so we're fine).
Set<?> s = (Set<?>)type.getSerializer().deserializeForNativeProtocol(value, version);
- Set<ByteBuffer> elements = new LinkedHashSet<ByteBuffer>(s.size());
+ SortedSet<ByteBuffer> elements = new TreeSet<ByteBuffer>(type.getElementsType());
for (Object element : s)
- elements.add(type.elements.decompose(element));
+ elements.add(type.getElementsType().decompose(element));
return new Value(elements);
}
catch (MarshalException e)
@@ -160,7 +154,12 @@ public abstract class Sets
public ByteBuffer get(QueryOptions options)
{
- return CollectionSerializer.pack(new ArrayList<ByteBuffer>(elements), elements.size(), options.getProtocolVersion());
+ return getWithProtocolVersion(options.getProtocolVersion());
+ }
+
+ public ByteBuffer getWithProtocolVersion(int protocolVersion)
+ {
+ return CollectionSerializer.pack(new ArrayList<>(elements), elements.size(), protocolVersion);
}
public boolean equals(SetType st, Value v)
@@ -170,8 +169,9 @@ public abstract class Sets
Iterator<ByteBuffer> thisIter = elements.iterator();
Iterator<ByteBuffer> thatIter = v.elements.iterator();
+ AbstractType elementsType = st.getElementsType();
while (thisIter.hasNext())
- if (st.elements.compare(thisIter.next(), thatIter.next()) != 0)
+ if (elementsType.compare(thisIter.next(), thatIter.next()) != 0)
return false;
return true;
@@ -202,7 +202,7 @@ public abstract class Sets
public Value bind(QueryOptions options) throws InvalidRequestException
{
- Set<ByteBuffer> buffers = new TreeSet<ByteBuffer>(comparator);
+ SortedSet<ByteBuffer> buffers = new TreeSet<>(comparator);
for (Term t : elements)
{
ByteBuffer bytes = t.bindAndGet(options);
@@ -246,9 +246,12 @@ public abstract class Sets
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
- // delete + add
- CellName name = cf.getComparator().create(prefix, column);
- cf.addAtom(params.makeTombstoneForOverwrite(name.slice()));
+ if (column.type.isMultiCell())
+ {
+ // delete + add
+ CellName name = cf.getComparator().create(prefix, column);
+ cf.addAtom(params.makeTombstoneForOverwrite(name.slice()));
+ }
Adder.doAdd(t, cf, prefix, column, params);
}
}
@@ -262,22 +265,35 @@ public abstract class Sets
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ assert column.type.isMultiCell() : "Attempted to add items to a frozen set";
+
doAdd(t, cf, prefix, column, params);
}
static void doAdd(Term t, ColumnFamily cf, Composite prefix, ColumnDefinition column, UpdateParameters params) throws InvalidRequestException
{
Term.Terminal value = t.bind(params.options);
- if (value == null)
- return;
-
- assert value instanceof Sets.Value : value;
+ Sets.Value setValue = (Sets.Value)value;
+ if (column.type.isMultiCell())
+ {
+ if (value == null)
+ return;
- Set<ByteBuffer> toAdd = ((Sets.Value)value).elements;
- for (ByteBuffer bb : toAdd)
+ Set<ByteBuffer> toAdd = setValue.elements;
+ for (ByteBuffer bb : toAdd)
+ {
+ CellName cellName = cf.getComparator().create(prefix, column, bb);
+ cf.addColumn(params.makeColumn(cellName, ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ }
+ }
+ else
{
- CellName cellName = cf.getComparator().create(prefix, column, bb);
- cf.addColumn(params.makeColumn(cellName, ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ // for frozen sets, we're overwriting the whole cell
+ CellName cellName = cf.getComparator().create(prefix, column);
+ if (value == null)
+ cf.addAtom(params.makeTombstone(cellName));
+ else
+ cf.addColumn(params.makeColumn(cellName, ((Value) value).getWithProtocolVersion(Server.CURRENT_VERSION)));
}
}
}
@@ -292,6 +308,8 @@ public abstract class Sets
public void execute(ByteBuffer rowKey, ColumnFamily cf, Composite prefix, UpdateParameters params) throws InvalidRequestException
{
+ assert column.type.isMultiCell() : "Attempted to remove items from a frozen set";
+
Term.Terminal value = t.bind(params.options);
if (value == null)
return;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/Term.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Term.java b/src/java/org/apache/cassandra/cql3/Term.java
index e5206c8..1587df1 100644
--- a/src/java/org/apache/cassandra/cql3/Term.java
+++ b/src/java/org/apache/cassandra/cql3/Term.java
@@ -138,6 +138,12 @@ public interface Term
public abstract List<ByteBuffer> getElements();
}
+ public interface CollectionTerminal
+ {
+ /** Gets the value of the collection when serialized with the given protocol version format */
+ public ByteBuffer getWithProtocolVersion(int protocolVersion);
+ }
+
/**
* A non terminal term, i.e. a term that can only be reduce to a byte buffer
* at execution time.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/Tuples.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Tuples.java b/src/java/org/apache/cassandra/cql3/Tuples.java
index 883cc60..4f1f141 100644
--- a/src/java/org/apache/cassandra/cql3/Tuples.java
+++ b/src/java/org/apache/cassandra/cql3/Tuples.java
@@ -248,13 +248,13 @@ public class Tuples
// but the deserialization does the validation (so we're fine).
List<?> l = (List<?>)type.getSerializer().deserializeForNativeProtocol(value, options.getProtocolVersion());
- assert type.elements instanceof TupleType;
- TupleType tupleType = (TupleType) type.elements;
+ assert type.getElementsType() instanceof TupleType;
+ TupleType tupleType = (TupleType) type.getElementsType();
// type.split(bytes)
List<List<ByteBuffer>> elements = new ArrayList<>(l.size());
for (Object element : l)
- elements.add(Arrays.asList(tupleType.split(type.elements.decompose(element))));
+ elements.add(Arrays.asList(tupleType.split(type.getElementsType().decompose(element))));
return new InValue(elements);
}
catch (MarshalException e)
@@ -337,15 +337,16 @@ public class Tuples
if (i < receivers.size() - 1)
inName.append(",");
- if (receiver.type instanceof CollectionType)
- throw new InvalidRequestException("Collection columns do not support IN relations");
+ if (receiver.type.isCollection() && receiver.type.isMultiCell())
+ throw new InvalidRequestException("Non-frozen collection columns do not support IN relations");
+
types.add(receiver.type);
}
inName.append(')');
ColumnIdentifier identifier = new ColumnIdentifier(inName.toString(), true);
TupleType type = new TupleType(types);
- return new ColumnSpecification(receivers.get(0).ksName, receivers.get(0).cfName, identifier, ListType.getInstance(type));
+ return new ColumnSpecification(receivers.get(0).ksName, receivers.get(0).cfName, identifier, ListType.getInstance(type, false));
}
public AbstractMarker prepare(String keyspace, List<? extends ColumnSpecification> receivers) throws InvalidRequestException
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/UntypedResultSet.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UntypedResultSet.java b/src/java/org/apache/cassandra/cql3/UntypedResultSet.java
index 42d0cb8..fef70fb 100644
--- a/src/java/org/apache/cassandra/cql3/UntypedResultSet.java
+++ b/src/java/org/apache/cassandra/cql3/UntypedResultSet.java
@@ -267,19 +267,19 @@ public abstract class UntypedResultSet implements Iterable<UntypedResultSet.Row>
public <T> Set<T> getSet(String column, AbstractType<T> type)
{
ByteBuffer raw = data.get(column);
- return raw == null ? null : SetType.getInstance(type).compose(raw);
+ return raw == null ? null : SetType.getInstance(type, true).compose(raw);
}
public <T> List<T> getList(String column, AbstractType<T> type)
{
ByteBuffer raw = data.get(column);
- return raw == null ? null : ListType.getInstance(type).compose(raw);
+ return raw == null ? null : ListType.getInstance(type, true).compose(raw);
}
public <K, V> Map<K, V> getMap(String column, AbstractType<K> keyType, AbstractType<V> valueType)
{
ByteBuffer raw = data.get(column);
- return raw == null ? null : MapType.getInstance(keyType, valueType).compose(raw);
+ return raw == null ? null : MapType.getInstance(keyType, valueType, true).compose(raw);
}
public List<ColumnSpecification> getColumns()
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/UpdateParameters.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UpdateParameters.java b/src/java/org/apache/cassandra/cql3/UpdateParameters.java
index 62ec09c..74c3214 100644
--- a/src/java/org/apache/cassandra/cql3/UpdateParameters.java
+++ b/src/java/org/apache/cassandra/cql3/UpdateParameters.java
@@ -97,6 +97,6 @@ public class UpdateParameters
return Collections.emptyList();
CQL3Row row = prefetchedLists.get(rowKey);
- return row == null ? Collections.<Cell>emptyList() : row.getCollection(cql3ColumnName);
+ return row == null ? Collections.<Cell>emptyList() : row.getMultiCellColumn(cql3ColumnName);
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ee55f361/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java
index 9d66c16..c92bc49 100644
--- a/src/java/org/apache/cassandra/cql3/UserTypes.java
+++ b/src/java/org/apache/cassandra/cql3/UserTypes.java
@@ -24,6 +24,7 @@ import org.apache.cassandra.db.marshal.CollectionType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.transport.Server;
/**
* Static helper methods and classes for user types.
@@ -171,7 +172,7 @@ public abstract class UserTypes
buffers[i] = values.get(i).bindAndGet(options);
// Inside UDT values, we must force the serialization of collections to v3 whatever protocol
// version is in use since we're going to store directly that serialized value.
- if (version < 3 && type.fieldType(i).isCollection() && buffers[i] != null)
+ if (version < Server.VERSION_3 && type.fieldType(i).isCollection() && buffers[i] != null)
buffers[i] = ((CollectionType)type.fieldType(i)).getSerializer().reserializeToV3(buffers[i]);
}
return buffers;