mirror of https://github.com/apache/cassandra
Support for frozen collections
Patch by Tyler Hobbs; reviewed by Sylvain Lebresne for CASSANDRA-7859
This commit is contained in:
parent
0337620994
commit
ee55f361b7
|
|
@ -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:
|
||||
|
|
|
|||
18
bin/cqlsh
18
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 '':
|
||||
|
|
|
|||
|
|
@ -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> )? )?
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 void freeze() throws InvalidRequestException
|
||||
{
|
||||
for (CQL3Type.Raw t : types)
|
||||
{
|
||||
if (t.supportsFreezing())
|
||||
t.freeze();
|
||||
}
|
||||
frozen = true;
|
||||
}
|
||||
|
||||
public CQL3Type prepare(String keyspace) throws InvalidRequestException
|
||||
{
|
||||
if (!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());
|
||||
|
||||
if (!frozen)
|
||||
throw new InvalidRequestException("Non-frozen tuples are not supported, please use frozen<>");
|
||||
|
||||
}
|
||||
return new Tuple(new TupleType(ts));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
MapType mapType = (MapType) column.type;
|
||||
AbstractType<?> valueType = mapType.getValuesType();
|
||||
if (column.type.isMultiCell())
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
for (ByteBuffer value : inValues)
|
||||
{
|
||||
if (isSatisfiedByValue(value, item, valueType, Operator.EQ, now))
|
||||
if (compareWithOperator(Operator.EQ, elementsType, value, columnValue))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
assert column.type instanceof ListType;
|
||||
ByteBuffer columnValue = ElementAccessBound.getListItem(
|
||||
collectionColumns(nameType.create(rowPrefix, column), current, now),
|
||||
ElementAccessBound.getListIndex(collectionElement));
|
||||
|
||||
AbstractType<?> valueType = ((ListType) column.type).elements;
|
||||
for (ByteBuffer value : inValues)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
{
|
||||
CellName cellName = cf.getComparator().create(prefix, column, entry.getKey());
|
||||
cf.addColumn(params.makeColumn(cellName, entry.getValue()));
|
||||
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
|
||||
{
|
||||
// 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");
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
Set<ByteBuffer> toAdd = ((Sets.Value)value).elements;
|
||||
for (ByteBuffer bb : toAdd)
|
||||
Sets.Value setValue = (Sets.Value)value;
|
||||
if (column.type.isMultiCell())
|
||||
{
|
||||
CellName cellName = cf.getComparator().create(prefix, column, bb);
|
||||
cf.addColumn(params.makeColumn(cellName, ByteBufferUtil.EMPTY_BYTE_BUFFER));
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
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
|
||||
{
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
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(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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,27 +76,20 @@ public abstract class CollectionType<T> extends AbstractType<T>
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompatibleWith(AbstractType<?> previous)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
public boolean isCollection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateCellValue(ByteBuffer cellValue) throws MarshalException
|
||||
{
|
||||
if (isMultiCell())
|
||||
valueComparator().validate(cellValue);
|
||||
else
|
||||
super.validateCellValue(cellValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this collection is Map.
|
||||
* @return <code>true</code> if this collection is a Map, <code>false</code> otherwise.
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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>>
|
||||
{
|
||||
// interning instances
|
||||
private static final Map<AbstractType<?>, ListType> instances = new HashMap<AbstractType<?>, ListType>();
|
||||
private static final Logger logger = LoggerFactory.getLogger(ListType.class);
|
||||
|
||||
public final AbstractType<T> elements;
|
||||
// interning instances
|
||||
private static final Map<AbstractType<?>, ListType> instances = new HashMap<>();
|
||||
private static final Map<AbstractType<?>, ListType> frozenInstances = new HashMap<>();
|
||||
|
||||
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()
|
||||
|
|
@ -78,6 +89,35 @@ public class ListType<T> extends CollectionType<List<T>>
|
|||
return serializer;
|
||||
}
|
||||
|
||||
@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)
|
||||
{
|
||||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -77,30 +90,66 @@ public class MapType<K, V> extends CollectionType<Map<K, V>>
|
|||
return values;
|
||||
}
|
||||
|
||||
@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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -71,6 +79,35 @@ public class SetType<T> extends CollectionType<Set<T>>
|
|||
return EmptyType.instance;
|
||||
}
|
||||
|
||||
@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)
|
||||
{
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
public static String stringifyCollectionsParameters(Map<ByteBuffer, CollectionType> collections)
|
||||
/**
|
||||
* 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, ? 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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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(", ");
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -310,6 +352,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
|
||||
{
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
@ -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>)");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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()];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue