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 19:44:02 UTC
[2/3] cassandra git commit: Support for frozen collections
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);
}
}