Merge branch 'cassandra-2.0' into cassandra-2.1 Conflicts: src/java/org/apache/cassandra/cql3/ColumnCondition.java
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/13510d41 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/13510d41 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/13510d41 Branch: refs/heads/trunk Commit: 13510d414fbb0da489ecd062054aa49d059ba79d Parents: 8234bc1 5aafa98 Author: Sylvain Lebresne <sylv...@datastax.com> Authored: Wed Apr 2 21:38:03 2014 +0200 Committer: Sylvain Lebresne <sylv...@datastax.com> Committed: Wed Apr 2 21:38:03 2014 +0200 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../apache/cassandra/cql3/ColumnCondition.java | 126 +++++++++++++++++-- src/java/org/apache/cassandra/cql3/Cql.g | 4 +- .../cql3/statements/CQL3CasConditions.java | 5 +- 4 files changed, 123 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/13510d41/CHANGES.txt ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/13510d41/src/java/org/apache/cassandra/cql3/ColumnCondition.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/ColumnCondition.java index 64c2032,9fb3390..67e7174 --- a/src/java/org/apache/cassandra/cql3/ColumnCondition.java +++ b/src/java/org/apache/cassandra/cql3/ColumnCondition.java @@@ -36,19 -35,28 +37,28 @@@ import org.apache.cassandra.utils.ByteB */ public class ColumnCondition { - public final CFDefinition.Name column; + public final ColumnDefinition column; + + // For collection, when testing the equality of a specific element, null otherwise. + private final Term collectionElement; + private final Term value; - private ColumnCondition(ColumnDefinition column, Term value) - private ColumnCondition(CFDefinition.Name column, Term collectionElement, Term value) ++ private ColumnCondition(ColumnDefinition column, Term collectionElement, Term value) { this.column = column; + this.collectionElement = collectionElement; this.value = value; } - // The only ones we support so far - public static ColumnCondition equal(CFDefinition.Name column, Term value) + public static ColumnCondition equal(ColumnDefinition column, Term value) { - return new ColumnCondition(column, value); + return new ColumnCondition(column, null, value); + } + - public static ColumnCondition equal(CFDefinition.Name column, Term collectionElement, Term value) ++ public static ColumnCondition equal(ColumnDefinition column, Term collectionElement, Term value) + { + return new ColumnCondition(column, collectionElement, value); } /** @@@ -76,15 -86,29 +88,29 @@@ this.variables = variables; } - // Not overriding equals() because we need the variables to have been attached when this is - // called and so having a non standard method name might help avoid mistakes public boolean equalsTo(WithVariables other) throws InvalidRequestException { - return column.equals(other.column()) - && value.bindAndGet(variables).equals(other.value().bindAndGet(other.variables)); + if (!column().equals(other.column())) + return false; + + if ((collectionElement() == null) != (other.collectionElement() == null)) + return false; + + if (collectionElement() != null) + { + assert column.type instanceof ListType || column.type instanceof MapType; + AbstractType<?> comparator = column.type instanceof ListType + ? Int32Type.instance + : ((MapType)column.type).keys; + + if (comparator.compare(collectionElement().bindAndGet(variables), other.collectionElement().bindAndGet(variables)) != 0) + return false; + } + + return value().bindAndGet(variables).equals(other.value().bindAndGet(other.variables)); } - private CFDefinition.Name column() + private ColumnDefinition column() { return column; } @@@ -94,6 -123,16 +125,11 @@@ return value; } + public ByteBuffer getCollectionElementValue() throws InvalidRequestException + { + return collectionElement == null ? null : collectionElement.bindAndGet(variables); + } + - private ColumnNameBuilder copyOrUpdatePrefix(CFMetaData cfm, ColumnNameBuilder rowPrefix) - { - return column.kind == CFDefinition.Name.Kind.STATIC ? cfm.getStaticColumnNameBuilder() : rowPrefix.copy(); - } - /** * Validates whether this condition applies to {@code current}. */ @@@ -102,22 -141,39 +138,34 @@@ if (column.type instanceof CollectionType) return collectionAppliesTo((CollectionType)column.type, rowPrefix, current, now); + assert collectionElement == null; - ColumnNameBuilder prefix = copyOrUpdatePrefix(current.metadata(), rowPrefix); - ByteBuffer columnName = column.kind == CFDefinition.Name.Kind.VALUE_ALIAS - ? prefix.build() - : prefix.add(column.name.key).build(); - - Column c = current.getColumn(columnName); + Cell c = current.getColumn(current.metadata().comparator.create(rowPrefix, column)); ByteBuffer v = value.bindAndGet(variables); return v == null ? c == null || !c.isLive(now) - : c != null && c.isLive(now) && column.type.compare(c.value(), v) == 0; + : c != null && c.isLive(now) && c.value().equals(v); } - private boolean collectionAppliesTo(CollectionType type, ColumnNameBuilder rowPrefix, ColumnFamily current, final long now) throws InvalidRequestException + private boolean collectionAppliesTo(CollectionType type, Composite rowPrefix, ColumnFamily current, final long now) throws InvalidRequestException { - ColumnNameBuilder collectionPrefix = copyOrUpdatePrefix(current.metadata(), rowPrefix).add(column.name.key); + Term.Terminal v = value.bind(variables); + + // For map element access, we won't iterate over the collection, so deal with that first. In other case, we do. + if (collectionElement != null && type instanceof MapType) + { + ByteBuffer e = collectionElement.bindAndGet(variables); + if (e == null) + throw new InvalidRequestException("Invalid null value for map access"); - return mapElementAppliesTo((MapType)type, current, collectionPrefix, e, v.get(), now); ++ return mapElementAppliesTo((MapType)type, current, rowPrefix, e, v.get(), now); + } + + CellName name = current.metadata().comparator.create(rowPrefix, column); // We are testing for collection equality, so we need to have the expected values *and* only those. - ColumnSlice[] collectionSlice = new ColumnSlice[]{ new ColumnSlice(collectionPrefix.build(), collectionPrefix.buildAsEndOfRange()) }; + ColumnSlice[] collectionSlice = new ColumnSlice[]{ name.slice() }; // Filter live columns, this makes things simpler afterwards - Iterator<Column> iter = Iterators.filter(current.iterator(collectionSlice), new Predicate<Column>() + Iterator<Cell> iter = Iterators.filter(current.iterator(collectionSlice), new Predicate<Cell>() { - public boolean apply(Column c) + public boolean apply(Cell c) { // we only care about live columns return c.isLive(now); @@@ -127,12 -183,21 +175,21 @@@ if (v == null) return !iter.hasNext(); + if (collectionElement != null) + { + assert type instanceof ListType; + ByteBuffer e = collectionElement.bindAndGet(variables); + if (e == null) + throw new InvalidRequestException("Invalid null value for list access"); + + return listElementAppliesTo((ListType)type, iter, e, v.get()); + } + switch (type.kind) { - case LIST: return listAppliesTo((ListType)type, current.metadata(), iter, ((Lists.Value)v).elements); - case SET: return setAppliesTo((SetType)type, current.metadata(), iter, ((Sets.Value)v).elements); - case MAP: return mapAppliesTo((MapType)type, current.metadata(), iter, ((Maps.Value)v).map); + case LIST: return listAppliesTo((ListType)type, iter, ((Lists.Value)v).elements); + case SET: return setAppliesTo((SetType)type, iter, ((Sets.Value)v).elements); + case MAP: return mapAppliesTo((MapType)type, iter, ((Maps.Value)v).map); } throw new AssertionError(); } @@@ -146,7 -217,20 +203,20 @@@ return !iter.hasNext(); } - private boolean listElementAppliesTo(ListType type, Iterator<Column> iter, ByteBuffer element, ByteBuffer value) throws InvalidRequestException ++ private boolean listElementAppliesTo(ListType type, Iterator<Cell> iter, ByteBuffer element, ByteBuffer value) throws InvalidRequestException + { + int idx = ByteBufferUtil.toInt(element); + if (idx < 0) + throw new InvalidRequestException(String.format("Invalid negative list index %d", idx)); + + int adv = Iterators.advance(iter, idx); + if (adv != idx || !iter.hasNext()) + throw new InvalidRequestException(String.format("List index %d out of bound, list has size %d", idx, adv)); + + return type.elements.compare(iter.next().value(), value) == 0; + } + - private boolean setAppliesTo(SetType type, CFMetaData cfm, Iterator<Column> iter, Set<ByteBuffer> elements) + private boolean setAppliesTo(SetType type, Iterator<Cell> iter, Set<ByteBuffer> elements) { Set<ByteBuffer> remaining = new TreeSet<>(type.elements); remaining.addAll(elements); @@@ -177,23 -261,59 +247,59 @@@ } return remaining.isEmpty(); } + - private boolean mapElementAppliesTo(MapType type, ColumnFamily current, ColumnNameBuilder collectionPrefix, ByteBuffer element, ByteBuffer value, long now) ++ private boolean mapElementAppliesTo(MapType type, ColumnFamily current, Composite rowPrefix, ByteBuffer element, ByteBuffer value, long now) + { - ByteBuffer name = collectionPrefix.add(element).build(); - Column c = current.getColumn(name); ++ CellName name = current.getComparator().create(rowPrefix, column, element); ++ Cell c = current.getColumn(name); + return c != null && c.isLive(now) && type.values.compare(c.value(), value) == 0; + } } public static class Raw { private final Term.Raw value; - public Raw(Term.Raw value) + // Can be null, only used with the syntax "IF m[e] = ..." (in which case it's 'e') + private final Term.Raw collectionElement; + + private Raw(Term.Raw value, Term.Raw collectionElement) { this.value = value; + this.collectionElement = collectionElement; + } + + public static Raw simpleEqual(Term.Raw value) + { + return new Raw(value, null); + } + + public static Raw collectionEqual(Term.Raw value, Term.Raw collectionElement) + { + return new Raw(value, collectionElement); } - public ColumnCondition prepare(CFDefinition.Name receiver) throws InvalidRequestException + public ColumnCondition prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException { if (receiver.type instanceof CounterColumnType) throw new InvalidRequestException("Condtions on counters are not supported"); - return ColumnCondition.equal(receiver, value.prepare(keyspace, receiver)); + if (collectionElement == null) - return ColumnCondition.equal(receiver, value.prepare(receiver)); ++ return ColumnCondition.equal(receiver, value.prepare(keyspace, receiver)); + + if (!(receiver.type.isCollection())) + throw new InvalidRequestException(String.format("Invalid element access syntax for non-collection column %s", receiver.name)); + + switch (((CollectionType)receiver.type).kind) + { + case LIST: - return ColumnCondition.equal(receiver, collectionElement.prepare(Lists.indexSpecOf(receiver)), value.prepare(Lists.valueSpecOf(receiver))); ++ return ColumnCondition.equal(receiver, collectionElement.prepare(keyspace, Lists.indexSpecOf(receiver)), value.prepare(keyspace, Lists.valueSpecOf(receiver))); + case SET: + throw new InvalidRequestException(String.format("Invalid element access syntax for set column %s", receiver.name)); + case MAP: - return ColumnCondition.equal(receiver, collectionElement.prepare(Maps.keySpecOf(receiver)), value.prepare(Maps.valueSpecOf(receiver))); ++ return ColumnCondition.equal(receiver, collectionElement.prepare(keyspace, Maps.keySpecOf(receiver)), value.prepare(keyspace, Maps.valueSpecOf(receiver))); + } + throw new AssertionError(); } } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/13510d41/src/java/org/apache/cassandra/cql3/Cql.g ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/13510d41/src/java/org/apache/cassandra/cql3/statements/CQL3CasConditions.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/CQL3CasConditions.java index fb842a2,9f67bc0..4003edc --- a/src/java/org/apache/cassandra/cql3/statements/CQL3CasConditions.java +++ b/src/java/org/apache/cassandra/cql3/statements/CQL3CasConditions.java @@@ -166,9 -167,9 +167,9 @@@ public class CQL3CasConditions implemen private static class ColumnsConditions extends RowCondition { - private final Map<ColumnIdentifier, ColumnCondition.WithVariables> conditions = new HashMap<>(); + private final Map<Pair<ColumnIdentifier, ByteBuffer>, ColumnCondition.WithVariables> conditions = new HashMap<>(); - private ColumnsConditions(ColumnNameBuilder rowPrefix, long now) + private ColumnsConditions(Composite rowPrefix, long now) { super(rowPrefix, now); }