blob: 8ba2665216491c66e8a0c3cec100b3226ebc2990 [file] [log] [blame]
/*
* * 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.filter;
import java.nio.ByteBuffer;
import java.util.*;
import org.junit.Test;
import org.apache.cassandra.db.composites.*;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.io.sstable.ColumnNameHelper;
import org.apache.cassandra.utils.ByteBufferUtil;
import static org.junit.Assert.*;
public class ColumnSliceTest
{
private static final CellNameType simpleIntType = new SimpleDenseCellNameType(Int32Type.instance);
@Test
public void testIntersectsSingleSlice()
{
List<AbstractType<?>> types = new ArrayList<>();
types.add(Int32Type.instance);
types.add(Int32Type.instance);
types.add(Int32Type.instance);
CompoundDenseCellNameType nameType = new CompoundDenseCellNameType(types);
// filter falls entirely before sstable
ColumnSlice slice = new ColumnSlice(composite(0, 0, 0), composite(1, 0, 0));
assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
// same case, but with empty start
slice = new ColumnSlice(composite(), composite(1, 0, 0));
assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
// same case, but with missing components for start
slice = new ColumnSlice(composite(0), composite(1, 0, 0));
assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
// same case, but with missing components for start and end
slice = new ColumnSlice(composite(0), composite(1, 0));
assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
// end of slice matches start of sstable for the first component, but not the second component
slice = new ColumnSlice(composite(0, 0, 0), composite(1, 0, 0));
assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), nameType, false));
// same case, but with missing components for start
slice = new ColumnSlice(composite(0), composite(1, 0, 0));
assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), nameType, false));
// same case, but with missing components for start and end
slice = new ColumnSlice(composite(0), composite(1, 0));
assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), nameType, false));
// first two components match, but not the last
slice = new ColumnSlice(composite(0, 0, 0), composite(1, 1, 0));
assertFalse(slice.intersects(columnNames(1, 1, 1), columnNames(3, 1, 1), nameType, false));
// all three components in slice end match the start of the sstable
slice = new ColumnSlice(composite(0, 0, 0), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 1, 1), columnNames(3, 1, 1), nameType, false));
// filter falls entirely after sstable
slice = new ColumnSlice(composite(4, 0, 0), composite(4, 0, 0));
assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
// same case, but with empty end
slice = new ColumnSlice(composite(4, 0, 0), composite());
assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
// same case, but with missing components for end
slice = new ColumnSlice(composite(4, 0, 0), composite(1));
assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
// same case, but with missing components for start and end
slice = new ColumnSlice(composite(4, 0), composite(1));
assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, false));
// start of slice matches end of sstable for the first component, but not the second component
slice = new ColumnSlice(composite(1, 1, 1), composite(2, 0, 0));
assertFalse(slice.intersects(columnNames(0, 0, 0), columnNames(1, 0, 0), nameType, false));
// start of slice matches end of sstable for the first two components, but not the last component
slice = new ColumnSlice(composite(1, 1, 1), composite(2, 0, 0));
assertFalse(slice.intersects(columnNames(0, 0, 0), columnNames(1, 1, 0), nameType, false));
// all three components in the slice start match the end of the sstable
slice = new ColumnSlice(composite(1, 1, 1), composite(2, 0, 0));
assertTrue(slice.intersects(columnNames(0, 0, 0), columnNames(1, 1, 1), nameType, false));
// slice covers entire sstable (with no matching edges)
slice = new ColumnSlice(composite(0, 0, 0), composite(2, 0, 0));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// same case, but with empty ends
slice = new ColumnSlice(composite(), composite());
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// same case, but with missing components
slice = new ColumnSlice(composite(0), composite(2, 0));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// slice covers entire sstable (with matching start)
slice = new ColumnSlice(composite(1, 0, 0), composite(2, 0, 0));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// slice covers entire sstable (with matching end)
slice = new ColumnSlice(composite(0, 0, 0), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// slice covers entire sstable (with matching start and end)
slice = new ColumnSlice(composite(1, 0, 0), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// slice falls entirely within sstable (with matching start)
slice = new ColumnSlice(composite(1, 0, 0), composite(1, 1, 0));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// same case, but with a missing end component
slice = new ColumnSlice(composite(1, 0, 0), composite(1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// slice falls entirely within sstable (with matching end)
slice = new ColumnSlice(composite(1, 1, 0), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// same case, but with a missing start component
slice = new ColumnSlice(composite(1, 1), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(1, 1, 1), nameType, false));
// slice falls entirely within sstable
slice = new ColumnSlice(composite(1, 1, 0), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, false));
// same case, but with a missing start component
slice = new ColumnSlice(composite(1, 1), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, false));
// same case, but with a missing start and end components
slice = new ColumnSlice(composite(1), composite(1, 2));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, false));
// same case, but with an equal first component and missing start and end components
slice = new ColumnSlice(composite(1), composite(1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, false));
// slice falls entirely within sstable (slice start and end are the same)
slice = new ColumnSlice(composite(1, 1, 1), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, false));
// slice starts within sstable, empty end
slice = new ColumnSlice(composite(1, 1, 1), composite());
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// same case, but with missing end components
slice = new ColumnSlice(composite(1, 1, 1), composite(3));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// slice starts within sstable (matching sstable start), empty end
slice = new ColumnSlice(composite(1, 0, 0), composite());
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// same case, but with missing end components
slice = new ColumnSlice(composite(1, 0, 0), composite(3));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// slice starts within sstable (matching sstable end), empty end
slice = new ColumnSlice(composite(2, 0, 0), composite());
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// same case, but with missing end components
slice = new ColumnSlice(composite(2, 0, 0), composite(3));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// slice ends within sstable, empty end
slice = new ColumnSlice(composite(), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// same case, but with missing start components
slice = new ColumnSlice(composite(0), composite(1, 1, 1));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// slice ends within sstable (matching sstable start), empty start
slice = new ColumnSlice(composite(), composite(1, 0, 0));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// same case, but with missing start components
slice = new ColumnSlice(composite(0), composite(1, 0, 0));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// slice ends within sstable (matching sstable end), empty start
slice = new ColumnSlice(composite(), composite(2, 0, 0));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// same case, but with missing start components
slice = new ColumnSlice(composite(0), composite(2, 0, 0));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 0, 0), nameType, false));
// the slice technically falls within the sstable range, but since the first component is restricted to
// a single value, we can check that the second component does not fall within its min/max
slice = new ColumnSlice(composite(1, 2, 0), composite(1, 3, 0));
assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
// same case, but with a missing start component
slice = new ColumnSlice(composite(1, 2), composite(1, 3, 0));
assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
// same case, but with a missing end component
slice = new ColumnSlice(composite(1, 2, 0), composite(1, 3));
assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
// same case, but with a missing start and end components
slice = new ColumnSlice(composite(1, 2), composite(1, 3));
assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
// same case, but with missing start and end components and different lengths for start and end
slice = new ColumnSlice(composite(1, 2), composite(1));
assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
// same as the previous set of tests, but the second component is equal in the slice start and end
slice = new ColumnSlice(composite(1, 2, 0), composite(1, 2, 0));
assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
// same case, but with a missing start component
slice = new ColumnSlice(composite(1, 2), composite(1, 2, 0));
assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
// same case, but with a missing end component
slice = new ColumnSlice(composite(1, 2, 0), composite(1, 2));
assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
// same case, but with a missing start and end components
slice = new ColumnSlice(composite(1, 2), composite(1, 2));
assertFalse(slice.intersects(columnNames(1, 0, 0), columnNames(2, 1, 0), nameType, false));
// same as the previous tests, but it's the third component that doesn't fit in its range this time
slice = new ColumnSlice(composite(1, 1, 2), composite(1, 1, 3));
assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(2, 2, 1), nameType, false));
// empty min/max column names
slice = new ColumnSlice(composite(), composite());
assertTrue(slice.intersects(columnNames(), columnNames(), nameType, false));
slice = new ColumnSlice(composite(1), composite());
assertTrue(slice.intersects(columnNames(), columnNames(), nameType, false));
slice = new ColumnSlice(composite(), composite(1));
assertTrue(slice.intersects(columnNames(), columnNames(), nameType, false));
slice = new ColumnSlice(composite(1), composite(1));
assertTrue(slice.intersects(columnNames(), columnNames(), nameType, false));
slice = new ColumnSlice(composite(), composite());
assertTrue(slice.intersects(columnNames(), columnNames(1), nameType, false));
slice = new ColumnSlice(composite(), composite(1));
assertTrue(slice.intersects(columnNames(), columnNames(1), nameType, false));
slice = new ColumnSlice(composite(), composite(1));
assertTrue(slice.intersects(columnNames(), columnNames(2), nameType, false));
slice = new ColumnSlice(composite(), composite(2));
assertTrue(slice.intersects(columnNames(), columnNames(1), nameType, false));
slice = new ColumnSlice(composite(2), composite(3));
assertFalse(slice.intersects(columnNames(), columnNames(1), nameType, false));
// basic check on reversed slices
slice = new ColumnSlice(composite(1, 0, 0), composite(0, 0, 0));
assertFalse(slice.intersects(columnNames(2, 0, 0), columnNames(3, 0, 0), nameType, true));
slice = new ColumnSlice(composite(1, 0, 0), composite(0, 0, 0));
assertFalse(slice.intersects(columnNames(1, 1, 0), columnNames(3, 0, 0), nameType, true));
slice = new ColumnSlice(composite(1, 1, 1), composite(1, 1, 0));
assertTrue(slice.intersects(columnNames(1, 0, 0), columnNames(2, 2, 2), nameType, true));
}
@Test
public void testDifferentMinMaxLengths()
{
List<AbstractType<?>> types = new ArrayList<>();
types.add(Int32Type.instance);
types.add(Int32Type.instance);
types.add(Int32Type.instance);
CompoundDenseCellNameType nameType = new CompoundDenseCellNameType(types);
// slice does intersect
ColumnSlice slice = new ColumnSlice(composite(), composite());
assertTrue(slice.intersects(columnNames(), columnNames(1), nameType, false));
slice = new ColumnSlice(composite(), composite());
assertTrue(slice.intersects(columnNames(1), columnNames(1, 2), nameType, false));
slice = new ColumnSlice(composite(), composite(1));
assertTrue(slice.intersects(columnNames(), columnNames(1), nameType, false));
slice = new ColumnSlice(composite(1), composite());
assertTrue(slice.intersects(columnNames(), columnNames(1), nameType, false));
slice = new ColumnSlice(composite(1), composite(1));
assertTrue(slice.intersects(columnNames(), columnNames(1), nameType, false));
slice = new ColumnSlice(composite(0), composite(1, 2, 3));
assertTrue(slice.intersects(columnNames(), columnNames(1), nameType, false));
slice = new ColumnSlice(composite(1, 2, 3), composite(2));
assertTrue(slice.intersects(columnNames(), columnNames(1), nameType, false));
// slice does not intersect
slice = new ColumnSlice(composite(2), composite(3, 4, 5));
assertFalse(slice.intersects(columnNames(), columnNames(1), nameType, false));
slice = new ColumnSlice(composite(0), composite(0, 1, 2));
assertFalse(slice.intersects(columnNames(1), columnNames(1, 2), nameType, false));
}
@Test
public void testColumnNameHelper()
{
List<AbstractType<?>> types = new ArrayList<>();
types.add(Int32Type.instance);
types.add(Int32Type.instance);
types.add(Int32Type.instance);
CompoundDenseCellNameType nameType = new CompoundDenseCellNameType(types);
assertTrue(ColumnNameHelper.overlaps(columnNames(0, 0, 0), columnNames(3, 3, 3), columnNames(1, 1, 1), columnNames(2, 2, 2), nameType));
assertFalse(ColumnNameHelper.overlaps(columnNames(0, 0, 0), columnNames(3, 3, 3), columnNames(4, 4, 4), columnNames(5, 5, 5), nameType));
assertFalse(ColumnNameHelper.overlaps(columnNames(0, 0, 0), columnNames(3, 3, 3), columnNames(3, 3, 4), columnNames(5, 5, 5), nameType));
assertTrue(ColumnNameHelper.overlaps(columnNames(0), columnNames(3, 3, 3), columnNames(1, 1), columnNames(5), nameType));
}
@Test
public void testDeoverlapSlices()
{
ColumnSlice[] slices;
ColumnSlice[] deoverlapped;
// Preserve correct slices
slices = slices(s(0, 3), s(4, 5), s(6, 9));
assertSlicesValid(slices);
assertSlicesEquals(slices, deoverlapSlices(slices));
// Simple overlap
slices = slices(s(0, 3), s(2, 5), s(8, 9));
assertSlicesInvalid(slices);
assertSlicesEquals(slices(s(0, 5), s(8, 9)), deoverlapSlices(slices));
// Slice overlaps others fully
slices = slices(s(0, 10), s(2, 5), s(8, 9));
assertSlicesInvalid(slices);
assertSlicesEquals(slices(s(0, 10)), deoverlapSlices(slices));
// Slice with empty end overlaps others fully
slices = slices(s(0, -1), s(2, 5), s(8, 9));
assertSlicesInvalid(slices);
assertSlicesEquals(slices(s(0, -1)), deoverlapSlices(slices));
// Overlap with slices selecting only one element
slices = slices(s(0, 4), s(4, 4), s(4, 8));
assertSlicesInvalid(slices);
assertSlicesEquals(slices(s(0, 8)), deoverlapSlices(slices));
// Unordered slices (without overlap)
slices = slices(s(4, 8), s(0, 3), s(9, 9));
assertSlicesInvalid(slices);
assertSlicesEquals(slices(s(0, 3), s(4, 8), s(9, 9)), deoverlapSlices(slices));
// All range select but not by a single slice
slices = slices(s(5, -1), s(2, 5), s(-1, 2));
assertSlicesInvalid(slices);
assertSlicesEquals(slices(s(-1, -1)), deoverlapSlices(slices));
}
@Test
public void testValidateSlices()
{
assertSlicesValid(slices(s(0, 3)));
assertSlicesValid(slices(s(3, 3)));
assertSlicesValid(slices(s(3, 3), s(4, 4)));
assertSlicesValid(slices(s(0, 3), s(4, 5), s(6, 9)));
assertSlicesValid(slices(s(-1, -1)));
assertSlicesValid(slices(s(-1, 3), s(4, -1)));
assertSlicesInvalid(slices(s(3, 0)));
assertSlicesInvalid(slices(s(0, 2), s(2, 4)));
assertSlicesInvalid(slices(s(0, 2), s(1, 4)));
assertSlicesInvalid(slices(s(0, 2), s(3, 4), s(3, 4)));
assertSlicesInvalid(slices(s(-1, 2), s(3, -1), s(5, 9)));
}
private static Composite composite(Integer ... components)
{
List<AbstractType<?>> types = new ArrayList<>();
types.add(Int32Type.instance);
types.add(Int32Type.instance);
types.add(Int32Type.instance);
CompoundDenseCellNameType nameType = new CompoundDenseCellNameType(types);
return nameType.make((Object[]) components);
}
private static List<ByteBuffer> columnNames(Integer ... components)
{
List<ByteBuffer> names = new ArrayList<>(components.length);
for (int component : components)
names.add(ByteBufferUtil.bytes(component));
return names;
}
private static Composite simpleComposite(int i)
{
// We special negative values to mean EMPTY for convenience sake
if (i < 0)
return Composites.EMPTY;
return simpleIntType.make(i);
}
private static ColumnSlice s(int start, int finish)
{
return new ColumnSlice(simpleComposite(start), simpleComposite(finish));
}
private static ColumnSlice[] slices(ColumnSlice... slices)
{
return slices;
}
private static ColumnSlice[] deoverlapSlices(ColumnSlice[] slices)
{
return ColumnSlice.deoverlapSlices(slices, simpleIntType);
}
private static void assertSlicesValid(ColumnSlice[] slices)
{
assertTrue("Slices " + toString(slices) + " should be valid", ColumnSlice.validateSlices(slices, simpleIntType, false));
}
private static void assertSlicesInvalid(ColumnSlice[] slices)
{
assertFalse("Slices " + toString(slices) + " shouldn't be valid", ColumnSlice.validateSlices(slices, simpleIntType, false));
}
private static void assertSlicesEquals(ColumnSlice[] expected, ColumnSlice[] actual)
{
assertTrue("Expected " + toString(expected) + " but got " + toString(actual), Arrays.equals(expected, actual));
}
private static String toString(ColumnSlice[] slices)
{
StringBuilder sb = new StringBuilder().append("[");
for (int i = 0; i < slices.length; i++)
{
if (i > 0)
sb.append(", ");
ColumnSlice slice = slices[i];
sb.append("(");
sb.append(slice.start.isEmpty() ? "-1" : simpleIntType.getString(slice.start));
sb.append(", ");
sb.append(slice.finish.isEmpty() ? "-1" : simpleIntType.getString(slice.finish));
sb.append(")");
}
return sb.append("]").toString();
}
}