Repository: cassandra Updated Branches: refs/heads/trunk 258e59fcf -> 71778eec2
http://git-wip-us.apache.org/repos/asf/cassandra/blob/90a012a1/test/unit/org/apache/cassandra/cql3/statements/SelectStatementTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/statements/SelectStatementTest.java b/test/unit/org/apache/cassandra/cql3/statements/SelectStatementTest.java new file mode 100644 index 0000000..1a66002 --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/statements/SelectStatementTest.java @@ -0,0 +1,973 @@ +/* + * 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.junit.Test; + +import org.apache.cassandra.config.CFMetaData; +import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.CFDefinition.Name; +import org.apache.cassandra.db.ColumnFamilyType; +import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.db.marshal.CompositeType; +import org.apache.cassandra.db.marshal.Int32Type; +import org.apache.cassandra.exceptions.ConfigurationException; +import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.utils.ByteBufferUtil; + +import static org.junit.Assert.assertArrayEquals; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; + +public class SelectStatementTest +{ + @Test + public void testBuildBoundWithNoRestrictions() throws Exception + { + Restriction[] restrictions = new Restriction[2]; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + } + + /** + * Test 'clustering_0 = 1' with only one clustering column + */ + @Test + public void testBuildBoundWithOneEqRestrictionsAndOneClusteringColumn() throws Exception + { + ByteBuffer clustering_0 = ByteBufferUtil.bytes(1); + SingleColumnRestriction.EQ eq = new SingleColumnRestriction.EQ(toTerm(clustering_0), false); + Restriction[] restrictions = new Restriction[] { eq }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), clustering_0); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), clustering_0); + } + + /** + * Test 'clustering_1 = 1' with 2 clustering columns + */ + @Test + public void testBuildBoundWithOneEqRestrictionsAndTwoClusteringColumns() throws Exception + { + ByteBuffer clustering_0 = ByteBufferUtil.bytes(1); + SingleColumnRestriction.EQ eq = new SingleColumnRestriction.EQ(toTerm(clustering_0), false); + Restriction[] restrictions = new Restriction[] { eq, null }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), clustering_0); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertEndOfRangeComposite(cfDef, bounds.get(0), clustering_0); + } + + /** + * Test 'clustering_0 IN (1, 2, 3)' with only one clustering column + */ + @Test + public void testBuildBoundWithOneInRestrictionsAndOneClusteringColumn() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + ByteBuffer value3 = ByteBufferUtil.bytes(3); + SingleColumnRestriction.IN in = new SingleColumnRestriction.InWithValues(toTerms(value1, value2, value3)); + Restriction[] restrictions = new Restriction[] { in }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(3, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1); + assertComposite(cfDef, bounds.get(1), value2); + assertComposite(cfDef, bounds.get(2), value3); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(3, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1); + assertComposite(cfDef, bounds.get(1), value2); + assertComposite(cfDef, bounds.get(2), value3); + } + + /** + * Test slice restriction (e.g 'clustering_0 > 1') with only one clustering column + */ + @Test + public void testBuildBoundWithSliceRestrictionsAndOneClusteringColumn() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + + SingleColumnRestriction.Slice slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GT, toTerm(value1)); + Restriction[] restrictions = new Restriction[] { slice }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value1); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GTE, toTerm(value1)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GTE, value1); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.LTE, toTerm(value1)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LTE, value1); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.LT, toTerm(value1)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LT, value1); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GT, toTerm(value1)); + slice.setBound(Relation.Type.LT, toTerm(value2)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value1); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LT, value2); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GTE, toTerm(value1)); + slice.setBound(Relation.Type.LTE, toTerm(value2)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GTE, value1); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LTE, value2); + } +// + /** + * Test 'clustering_0 = 1 AND clustering_1 IN (1, 2, 3)' with two clustering columns + */ + @Test + public void testBuildBoundWithEqAndInRestrictions() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + ByteBuffer value3 = ByteBufferUtil.bytes(3); + SingleColumnRestriction.EQ eq = new SingleColumnRestriction.EQ(toTerm(value1), false); + SingleColumnRestriction.IN in = new SingleColumnRestriction.InWithValues(toTerms(value1, value2, value3)); + Restriction[] restrictions = new Restriction[] { eq, in }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(3, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value1); + assertComposite(cfDef, bounds.get(1), value1, value2); + assertComposite(cfDef, bounds.get(2), value1, value3); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(3, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value1); + assertComposite(cfDef, bounds.get(1), value1, value2); + assertComposite(cfDef, bounds.get(2), value1, value3); + } + + /** + * Test slice restriction (e.g 'clustering_0 > 1') with only one clustering column + */ + @Test + public void testBuildBoundWithEqAndSliceRestrictions() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + ByteBuffer value3 = ByteBufferUtil.bytes(3); + + SingleColumnRestriction.EQ eq = new SingleColumnRestriction.EQ(toTerm(value3), false); + + SingleColumnRestriction.Slice slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GT, toTerm(value1)); + Restriction[] restrictions = new Restriction[] { eq, slice }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value3, value1); + + bounds = executeBuildBound(cfDef,restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertEndOfRangeComposite(cfDef, bounds.get(0), value3); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GTE, toTerm(value1)); + restrictions = new Restriction[] { eq, slice }; + + bounds = executeBuildBound(cfDef,restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GTE, value3, value1); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertEndOfRangeComposite(cfDef, bounds.get(0), value3); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.LTE, toTerm(value1)); + restrictions = new Restriction[] { eq, slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value3); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LTE, value3, value1); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.LT, toTerm(value1)); + restrictions = new Restriction[] { eq, slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value3); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LT, value3, value1); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GT, toTerm(value1)); + slice.setBound(Relation.Type.LT, toTerm(value2)); + restrictions = new Restriction[] { eq, slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value3, value1); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LT, value3, value2); + + slice = new SingleColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GTE, toTerm(value1)); + slice.setBound(Relation.Type.LTE, toTerm(value2)); + restrictions = new Restriction[] { eq, slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GTE, value3, value1); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LTE, value3, value2); + } + + /** + * Test '(clustering_0, clustering_1) = (1, 2)' with two clustering column + */ + @Test + public void testBuildBoundWithMultiEqRestrictions() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + MultiColumnRestriction.EQ eq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false); + Restriction[] restrictions = new Restriction[] { eq, eq }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2); + } + + /** + * Test '(clustering_0, clustering_1) IN ((1, 2), (2, 3))' with two clustering column + */ + @Test + public void testBuildBoundWithMultiInRestrictions() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + ByteBuffer value3 = ByteBufferUtil.bytes(3); + List<Term> terms = asList(toMultiItemTerminal(value1, value2), toMultiItemTerminal(value2, value3)); + MultiColumnRestriction.IN in = new MultiColumnRestriction.InWithValues(terms); + Restriction[] restrictions = new Restriction[] { in, in }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(2, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2); + assertComposite(cfDef, bounds.get(1), value2, value3); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(2, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2); + assertComposite(cfDef, bounds.get(1), value2, value3); + } + + /** + * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column + */ + @Test + public void testBuildBoundWithMultiSliceRestrictionsWithOneClusteringColumn() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + + MultiColumnRestriction.Slice slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GT, toMultiItemTerminal(value1)); + Restriction[] restrictions = new Restriction[] { slice }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value1); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GTE, toMultiItemTerminal(value1)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GTE, value1); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.LTE, toMultiItemTerminal(value1)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LTE, value1); + + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.LT, toMultiItemTerminal(value1)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LT, value1); + + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GT, toMultiItemTerminal(value1)); + slice.setBound(Relation.Type.LT, toMultiItemTerminal(value2)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value1); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LT, value2); + + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GTE, toMultiItemTerminal(value1)); + slice.setBound(Relation.Type.LTE, toMultiItemTerminal(value2)); + restrictions = new Restriction[] { slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GTE, value1); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LTE, value2); + } + + /** + * Test multi-column slice restrictions (e.g '(clustering_0, clustering_1) > (1, 2)') with two clustering + * columns + */ + @Test + public void testBuildBoundWithMultiSliceRestrictionsWithTwoClusteringColumn() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + + // (clustering_0, clustering1) > (1, 2) + MultiColumnRestriction.Slice slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GT, toMultiItemTerminal(value1, value2)); + Restriction[] restrictions = new Restriction[] { slice, slice }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value1, value2); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + // (clustering_0, clustering1) >= (1, 2) + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GTE, toMultiItemTerminal(value1, value2)); + restrictions = new Restriction[] { slice, slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GTE, value1, value2); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + // (clustering_0, clustering1) <= (1, 2) + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.LTE, toMultiItemTerminal(value1, value2)); + restrictions = new Restriction[] { slice, slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LTE, value1, value2); + + // (clustering_0, clustering1) < (1, 2) + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.LT, toMultiItemTerminal(value1, value2)); + restrictions = new Restriction[] { slice, slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0)); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LT, value1, value2); + + // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2) + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GT, toMultiItemTerminal(value1, value2)); + slice.setBound(Relation.Type.LT, toMultiItemTerminal(value2)); + restrictions = new Restriction[] { slice, slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value1, value2); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LT, value2); + + // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1) + slice = new MultiColumnRestriction.Slice(false); + slice.setBound(Relation.Type.GTE, toMultiItemTerminal(value1, value2)); + slice.setBound(Relation.Type.LTE, toMultiItemTerminal(value2, value1)); + restrictions = new Restriction[] { slice, slice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GTE, value1, value2); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LTE, value2, value1); + } + + /** + * Test mixing single and multi equals restrictions (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3)) + */ + @Test + public void testBuildBoundWithSingleEqAndMultiEqRestrictions() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + ByteBuffer value3 = ByteBufferUtil.bytes(3); + ByteBuffer value4 = ByteBufferUtil.bytes(4); + + // clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3) + SingleColumnRestriction.EQ singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false); + MultiColumnRestriction.EQ multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value2, value3), false); + Restriction[] restrictions = new Restriction[] { singleEq, multiEq, multiEq }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3); + + // clustering_0 = 1 AND clustering_1 = 2 AND (clustering_2, clustering_3) = (3, 4) + singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false); + SingleColumnRestriction.EQ singleEq2 = new SingleColumnRestriction.EQ(toTerm(value2), false); + multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value3, value4), false); + restrictions = new Restriction[] { singleEq, singleEq2, multiEq, multiEq }; + cfDef = createCFDefinition(restrictions.length); + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3, value4); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3, value4); + + // (clustering_0, clustering_1) = (1, 2) AND clustering_2 = 3 + singleEq = new SingleColumnRestriction.EQ(toTerm(value3), false); + multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false); + restrictions = new Restriction[] { multiEq, multiEq, singleEq }; + cfDef = createCFDefinition(restrictions.length); + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3); + + // clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3) AND clustering_3 = 4 + singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false); + singleEq2 = new SingleColumnRestriction.EQ(toTerm(value4), false); + multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value2, value3), false); + restrictions = new Restriction[] { singleEq, multiEq, multiEq, singleEq2 }; + cfDef = createCFDefinition(restrictions.length); + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3, value4); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3, value4); + } + + /** + * Test clustering_0 = 1 AND (clustering_1, clustering_2) IN ((2, 3), (4, 5)) + */ + @Test + public void testBuildBoundWithSingleEqAndMultiINRestrictions() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + ByteBuffer value3 = ByteBufferUtil.bytes(3); + ByteBuffer value4 = ByteBufferUtil.bytes(4); + ByteBuffer value5 = ByteBufferUtil.bytes(5); + + // clustering_0 = 1 AND (clustering_1, clustering_2) IN ((2, 3), (4, 5)) + SingleColumnRestriction.EQ singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false); + MultiColumnRestriction.IN multiIn = + new MultiColumnRestriction.InWithValues(asList(toMultiItemTerminal(value2, value3), + toMultiItemTerminal(value4, value5))); + + Restriction[] restrictions = new Restriction[] { singleEq, multiIn, multiIn }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(2, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3); + assertComposite(cfDef, bounds.get(1), value1, value4, value5); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(2, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3); + assertComposite(cfDef, bounds.get(1), value1, value4, value5); + + // clustering_0 = 1 AND (clustering_1, clustering_2) IN ((2, 3)) + singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false); + multiIn = new MultiColumnRestriction.InWithValues(asList(toMultiItemTerminal(value2, value3), + toMultiItemTerminal(value4, value5))); + + restrictions = new Restriction[] { singleEq, multiIn, multiIn }; + cfDef = createCFDefinition(restrictions.length); + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(2, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3); + assertComposite(cfDef, bounds.get(1), value1, value4, value5); + + // clustering_0 = 1 AND clustering_1 = 5 AND (clustering_2, clustering_3) IN ((2, 3), (4, 5)) + singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false); + SingleColumnRestriction.EQ singleEq2 = new SingleColumnRestriction.EQ(toTerm(value5), false); + multiIn = new MultiColumnRestriction.InWithValues(asList(toMultiItemTerminal(value2, value3), + toMultiItemTerminal(value4, value5))); + + restrictions = new Restriction[] { singleEq, singleEq2, multiIn, multiIn }; + cfDef = createCFDefinition(restrictions.length); + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(2, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value5, value2, value3); + assertComposite(cfDef, bounds.get(1), value1, value5, value4, value5); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(2, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value5, value2, value3); + assertComposite(cfDef, bounds.get(1), value1, value5, value4, value5); + } + + /** + * Test mixing single equal restrictions with multi-column slice restrictions + * (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3)) + */ + @Test + public void testBuildBoundWithSingleEqAndSliceRestrictions() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + ByteBuffer value3 = ByteBufferUtil.bytes(3); + ByteBuffer value4 = ByteBufferUtil.bytes(4); + ByteBuffer value5 = ByteBufferUtil.bytes(5); + + // clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3) + SingleColumnRestriction.EQ singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false); + MultiColumnRestriction.Slice multiSlice = new MultiColumnRestriction.Slice(false); + multiSlice.setBound(Relation.Type.GT, toMultiItemTerminal(value2, value3)); + + Restriction[] restrictions = new Restriction[] { singleEq, multiSlice, multiSlice }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value1, value2, value3); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertEndOfRangeComposite(cfDef, bounds.get(0), value1); + + // clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3) AND (clustering_1) < (4) + singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false); + multiSlice = new MultiColumnRestriction.Slice(false); + multiSlice.setBound(Relation.Type.GT, toMultiItemTerminal(value2, value3)); + multiSlice.setBound(Relation.Type.LT, toMultiItemTerminal(value4)); + + restrictions = new Restriction[] { singleEq, multiSlice, multiSlice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value1, value2, value3); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LT, value1, value4); + + // clustering_0 = 1 AND (clustering_1, clustering_2) => (2, 3) AND (clustering_1, clustering_2) <= (4, 5) + singleEq = new SingleColumnRestriction.EQ(toTerm(value1), false); + multiSlice = new MultiColumnRestriction.Slice(false); + multiSlice.setBound(Relation.Type.GTE, toMultiItemTerminal(value2, value3)); + multiSlice.setBound(Relation.Type.LTE, toMultiItemTerminal(value4, value5)); + + restrictions = new Restriction[] { singleEq, multiSlice, multiSlice }; + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GTE, value1, value2, value3); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.LTE, value1, value4, value5); + } + + /** + * Test mixing multi equal restrictions with single-column slice restrictions + * (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3)) + */ + @Test + public void testBuildBoundWithMultiEqAndSingleSliceRestrictions() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + ByteBuffer value3 = ByteBufferUtil.bytes(3); + + // (clustering_0, clustering_1) = (1, 2) AND clustering_2 > 3 + MultiColumnRestriction.EQ multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false); + SingleColumnRestriction.Slice singleSlice = new SingleColumnRestriction.Slice(false); + singleSlice.setBound(Relation.Type.GT, toTerm(value3)); + + Restriction[] restrictions = new Restriction[] { multiEq, multiEq, singleSlice }; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT, value1, value2, value3); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertEndOfRangeComposite(cfDef, bounds.get(0), value1, value2); + } + + @Test + public void testBuildBoundWithSeveralMultiColumnRestrictions() throws Exception + { + ByteBuffer value1 = ByteBufferUtil.bytes(1); + ByteBuffer value2 = ByteBufferUtil.bytes(2); + ByteBuffer value3 = ByteBufferUtil.bytes(3); + ByteBuffer value4 = ByteBufferUtil.bytes(4); + ByteBuffer value5 = ByteBufferUtil.bytes(5); + + // (clustering_0, clustering_1) = (1, 2) AND (clustering_2, clustering_3) > (3, 4) + MultiColumnRestriction.EQ multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false); + MultiColumnRestriction.Slice multiSlice = new MultiColumnRestriction.Slice(false); + multiSlice.setBound(Relation.Type.GT, toMultiItemTerminal(value3, value4)); + + Restriction[] restrictions = new Restriction[] { multiEq, multiEq, multiSlice, multiSlice}; + CFDefinition cfDef = createCFDefinition(restrictions.length); + + List<ByteBuffer> bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertForRelationComposite(cfDef, bounds.get(0), Relation.Type.GT,value1, value2, value3, value4); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertEndOfRangeComposite(cfDef, bounds.get(0), value1, value2); + + // (clustering_0, clustering_1) = (1, 2) AND (clustering_2, clustering_3) IN ((3, 4), (4, 5)) + multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false); + MultiColumnRestriction.IN multiIn = + new MultiColumnRestriction.InWithValues(asList(toMultiItemTerminal(value3, value4), + toMultiItemTerminal(value4, value5))); + + restrictions = new Restriction[] { multiEq, multiEq, multiIn, multiIn}; + cfDef = createCFDefinition(restrictions.length); + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(2, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3, value4); + assertComposite(cfDef, bounds.get(1), value1, value2, value4, value5); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(2, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3, value4); + assertComposite(cfDef, bounds.get(1), value1, value2, value4, value5); + + // (clustering_0, clustering_1) = (1, 2) AND (clustering_2, clustering_3) = ((3, 4), (4, 5)) + multiEq = new MultiColumnRestriction.EQ(toMultiItemTerminal(value1, value2), false); + MultiColumnRestriction.EQ multiEq2 = new MultiColumnRestriction.EQ(toMultiItemTerminal(value3, value4), false); + + restrictions = new Restriction[] { multiEq, multiEq, multiEq2, multiEq2}; + cfDef = createCFDefinition(restrictions.length); + + bounds = executeBuildBound(cfDef, restrictions, Bound.START); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3, value4); + + bounds = executeBuildBound(cfDef, restrictions, Bound.END); + assertEquals(1, bounds.size()); + assertComposite(cfDef, bounds.get(0), value1, value2, value3, value4); + } + + /** + * Asserts that the specified composite contains the specified elements. + * + * @param cfDef the Column Family Definition + * @param actual the buffer to test + * @param elements the expected elements of the composite + */ + private static void assertComposite(CFDefinition cfDef, ByteBuffer actual, ByteBuffer... elements) + { + ColumnNameBuilder builder = addElements(cfDef.getColumnNameBuilder(), elements); + assertArrayEquals("the composite is not the expected one:", actual.array(), builder.build().array()); + } + + /** + * Asserts that the specified composite is an end of range composite that contains the specified elements. + * + * @param cfDef the Column Family Definition + * @param actual the buffer to test + * @param elements the expected elements of the composite + */ + private static void assertEndOfRangeComposite(CFDefinition cfDef, ByteBuffer actual, ByteBuffer... elements) + { + ColumnNameBuilder builder = addElements(cfDef.getColumnNameBuilder(), elements); + assertArrayEquals("the composite is not the expected one:", actual.array(), builder.buildAsEndOfRange().array()); + } + + /** + * Asserts that the specified composite is an end of range composite that contains the specified elements. + * + * @param cfDef the Column Family Definition + * @param actual the buffer to test + * @param elements the expected elements of the composite + */ + private static void assertForRelationComposite(CFDefinition cfDef, + ByteBuffer actual, + Relation.Type relType, + ByteBuffer... elements) + { + ColumnNameBuilder builder = addElements(cfDef.getColumnNameBuilder(), elements); + assertArrayEquals("the composite is not the expected one:", actual.array(), builder.buildForRelation(relType).array()); + } + + /** + * Adds all the specified elements to the specified builder. + * + * @param builder the builder to add to + * @param elements the elements to add + * @return the builder + */ + private static ColumnNameBuilder addElements(ColumnNameBuilder builder, ByteBuffer... elements) + { + for (int i = 0, m = elements.length; i < m; i++) + builder.add(elements[i]); + return builder; + } + + /** + * Calls the <code>SelectStatement.buildBound</code> with the specified restrictions. + * + * @param cfDef the Column Family Definition + * @param restrictions the restrictions + * @return the result from the method call to <code>SelectStatement.buildBound</code> + * @throws InvalidRequestException if buildBound throw an <code>Exception</code> + */ + private static List<ByteBuffer> executeBuildBound(CFDefinition cfDef, + Restriction[] restrictions, + Bound bound) throws InvalidRequestException + { + return SelectStatement.buildBound(bound, + new ArrayList<Name>(cfDef.clusteringColumns()), + restrictions, + false, + cfDef, + cfDef.getColumnNameBuilder(), + Collections.<ByteBuffer>emptyList()); + } + + /** + * Creates a <code>CFDefinition</code> to be used in the tests. + * + * @param numberOfClusteringColumns the number of clustering columns + * @return a new a <code>CFDefinition</code> instance + * @throws ConfigurationException if the CFDefinition cannot be created + */ + private static CFDefinition createCFDefinition(int numberOfClusteringColumns) throws ConfigurationException + { + List<AbstractType<?>> types = new ArrayList<>(); + for (int i = 0, m = numberOfClusteringColumns; i < m; i++) + types.add(Int32Type.instance); + + CompositeType cType = CompositeType.getInstance(types); + CFMetaData cfMetaData = new CFMetaData("keyspace", "test", ColumnFamilyType.Standard, cType); + ByteBuffer partitionKey = ByteBufferUtil.bytes("partitionKey"); + cfMetaData.addColumnDefinition(ColumnDefinition.partitionKeyDef(partitionKey, Int32Type.instance, 0)); + + for (int i = 0, m = numberOfClusteringColumns; i < m; i++) + { + ByteBuffer name = ByteBufferUtil.bytes("clustering_" + i); + cfMetaData.addColumnDefinition(ColumnDefinition.clusteringKeyDef(name, types.get(i), i)); + } + cfMetaData.rebuild(); + return new CFDefinition(cfMetaData); + } + + /** + * Converts the specified values into a <code>MultiItemTerminal</code>. + * + * @param values the values to convert. + * @return the term corresponding to the specified values. + */ + private static Term toMultiItemTerminal(ByteBuffer... values) + { + return new Tuples.Value(values); + } + + /** + * Converts the specified value into a term. + * + * @param value the value to convert. + * @return the term corresponding to the specified value. + */ + private static Term toTerm(ByteBuffer value) + { + return new Constants.Value(value); + } + + /** + * Converts the specified values into a <code>List</code> of terms. + * + * @param values the values to convert. + * @return a <code>List</code> of terms corresponding to the specified values. + */ + private static List<Term> toTerms(ByteBuffer... values) + { + List<Term> terms = new ArrayList<>(); + for (ByteBuffer value : values) + terms.add(toTerm(value)); + return terms; + } +}
