Merge branch 'cassandra-2.0' into trunk Conflicts: src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/1c985830 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/1c985830 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/1c985830 Branch: refs/heads/trunk Commit: 1c9858306517ddecc5dd9d17b85d20a9724afe72 Parents: 5102e8d e93578b Author: Sylvain Lebresne <sylv...@datastax.com> Authored: Wed Sep 18 17:59:17 2013 +0200 Committer: Sylvain Lebresne <sylv...@datastax.com> Committed: Wed Sep 18 17:59:17 2013 +0200 ---------------------------------------------------------------------- CHANGES.txt | 1 + doc/cql3/CQL.textile | 3 + .../apache/cassandra/cql3/AbstractMarker.java | 25 ++ src/java/org/apache/cassandra/cql3/Cql.g | 1 + .../org/apache/cassandra/cql3/Relation.java | 2 +- .../apache/cassandra/cql3/statements/Bound.java | 35 ++ .../cql3/statements/ModificationStatement.java | 69 ++-- .../cassandra/cql3/statements/Restriction.java | 289 +++++++++++++++ .../cql3/statements/SelectStatement.java | 348 ++++++------------- 9 files changed, 511 insertions(+), 262 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/1c985830/CHANGES.txt ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/1c985830/src/java/org/apache/cassandra/cql3/statements/Restriction.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/Restriction.java index 0000000,45cc607..73444ad mode 000000,100644..100644 --- a/src/java/org/apache/cassandra/cql3/statements/Restriction.java +++ b/src/java/org/apache/cassandra/cql3/statements/Restriction.java @@@ -1,0 -1,289 +1,289 @@@ + /* + * 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.statements; + + import java.nio.ByteBuffer; + import java.util.ArrayList; + import java.util.Collections; + import java.util.List; + + import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.thrift.IndexOperator; ++import org.apache.cassandra.db.IndexExpression; + import org.apache.cassandra.cql3.*; + + /** + * A restriction/clause on a column. + * The goal of this class being to group all conditions for a column in a SELECT. + */ + public interface Restriction + { + public boolean isOnToken(); + + public boolean isSlice(); + public boolean isEQ(); + public boolean isIN(); + + // Only supported for EQ and IN, but it's convenient to have here + public List<ByteBuffer> values(List<ByteBuffer> variables) throws InvalidRequestException; + + public static class EQ implements Restriction + { + private final Term value; + private final boolean onToken; + + public EQ(Term value, boolean onToken) + { + this.value = value; + this.onToken = onToken; + } + + public List<ByteBuffer> values(List<ByteBuffer> variables) throws InvalidRequestException + { + return Collections.singletonList(value.bindAndGet(variables)); + } + + public boolean isSlice() + { + return false; + } + + public boolean isEQ() + { + return true; + } + + public boolean isIN() + { + return false; + } + + public boolean isOnToken() + { + return onToken; + } + + @Override + public String toString() + { + return String.format("EQ(%s)%s", value, onToken ? "*" : ""); + } + } + + public static abstract class IN implements Restriction + { + public static IN create(List<Term> values) + { + return new WithValues(values); + } + + public static IN create(Term value) throws InvalidRequestException + { + assert value instanceof Lists.Marker; // we shouldn't have got there otherwise + return new WithMarker((Lists.Marker)value); + } + + public boolean isSlice() + { + return false; + } + + public boolean isEQ() + { + return false; + } + + public boolean isIN() + { + return true; + } + + public boolean isOnToken() + { + return false; + } + + private static class WithValues extends IN + { + private final List<Term> values; + + private WithValues(List<Term> values) + { + this.values = values; + } + + public List<ByteBuffer> values(List<ByteBuffer> variables) throws InvalidRequestException + { + List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(values.size()); + for (Term value : values) + buffers.add(value.bindAndGet(variables)); + return buffers; + } + + @Override + public String toString() + { + return String.format("IN(%s)", values); + } + } + + private static class WithMarker extends IN + { + private final Lists.Marker marker; + + private WithMarker(Lists.Marker marker) + { + this.marker = marker; + } + + public List<ByteBuffer> values(List<ByteBuffer> variables) throws InvalidRequestException + { + Lists.Value lval = marker.bind(variables); + if (lval == null) + throw new InvalidRequestException("Invalid null value for IN restriction"); + return lval.elements; + } + + @Override + public String toString() + { + return "IN ?"; + } + } + } + + public static class Slice implements Restriction + { + private final Term[] bounds; + private final boolean[] boundInclusive; + private final boolean onToken; + + public Slice(boolean onToken) + { + this.bounds = new Term[2]; + this.boundInclusive = new boolean[2]; + this.onToken = onToken; + } + + public boolean isSlice() + { + return true; + } + + public boolean isEQ() + { + return false; + } + + public boolean isIN() + { + return false; + } + + public List<ByteBuffer> values(List<ByteBuffer> variables) throws InvalidRequestException + { + throw new UnsupportedOperationException(); + } + + public boolean isOnToken() + { + return onToken; + } + + public boolean hasBound(Bound b) + { + return bounds[b.idx] != null; + } + + public ByteBuffer bound(Bound b, List<ByteBuffer> variables) throws InvalidRequestException + { + return bounds[b.idx].bindAndGet(variables); + } + + public boolean isInclusive(Bound b) + { + return bounds[b.idx] == null || boundInclusive[b.idx]; + } + + public Relation.Type getRelation(Bound eocBound, Bound inclusiveBound) + { + switch (eocBound) + { + case START: + return boundInclusive[inclusiveBound.idx] ? Relation.Type.GTE : Relation.Type.GT; + case END: + return boundInclusive[inclusiveBound.idx] ? Relation.Type.LTE : Relation.Type.LT; + } + throw new AssertionError(); + } + - public IndexOperator getIndexOperator(Bound b) ++ public IndexExpression.Operator getIndexOperator(Bound b) + { + switch (b) + { + case START: - return boundInclusive[b.idx] ? IndexOperator.GTE : IndexOperator.GT; ++ return boundInclusive[b.idx] ? IndexExpression.Operator.GTE : IndexExpression.Operator.GT; + case END: - return boundInclusive[b.idx] ? IndexOperator.LTE : IndexOperator.LT; ++ return boundInclusive[b.idx] ? IndexExpression.Operator.LTE : IndexExpression.Operator.LT; + } + throw new AssertionError(); + } + + public void setBound(ColumnIdentifier name, Relation.Type type, Term t) throws InvalidRequestException + { + Bound b; + boolean inclusive; + switch (type) + { + case GT: + b = Bound.START; + inclusive = false; + break; + case GTE: + b = Bound.START; + inclusive = true; + break; + case LT: + b = Bound.END; + inclusive = false; + break; + case LTE: + b = Bound.END; + inclusive = true; + break; + default: + throw new AssertionError(); + } + + if (bounds[b.idx] != null) + throw new InvalidRequestException(String.format("Invalid restrictions found on %s", name)); + + bounds[b.idx] = t; + boundInclusive[b.idx] = inclusive; + } + + @Override + public String toString() + { + return String.format("SLICE(%s %s, %s %s)%s", boundInclusive[0] ? ">=" : ">", + bounds[0], + boundInclusive[1] ? "<=" : "<", + bounds[1], + onToken ? "*" : ""); + } + } + } http://git-wip-us.apache.org/repos/asf/cassandra/blob/1c985830/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/SelectStatement.java index 8412dd4,c85864f..9a3ffc3 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@@ -772,6 -758,20 +756,20 @@@ public class SelectStatement implement } } } + else + { + List<ByteBuffer> values = restriction.values(variables); + + if (values.size() != 1) + throw new InvalidRequestException("IN restrictions are not supported on indexed columns"); + + ByteBuffer value = values.get(0); + if (value == null) + throw new InvalidRequestException(String.format("Unsupported null value for indexed column %s", name)); + if (value.remaining() > 0xFFFF) + throw new InvalidRequestException("Index expression values may not be larger than 64K"); - expressions.add(new IndexExpression(name.name.key, IndexOperator.EQ, value)); ++ expressions.add(new IndexExpression(name.name.key, IndexExpression.Operator.EQ, value)); + } } return expressions; }