blob: b762fa53476db3f9f322f0483f214b8fe5779fd4 [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.io.IOException;
import java.nio.ByteBuffer;
import java.util.Comparator;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.db.rows.CellPath;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.CollectionType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.utils.ByteBufferUtil;
/**
* Handles the selection of a subpart of a column.
* <p>
* This only make sense for complex column. For those, this allow for instance
* to select only a slice of a map.
*/
public abstract class ColumnSubselection implements Comparable<ColumnSubselection>
{
public static final Serializer serializer = new Serializer();
private enum Kind { SLICE, ELEMENT }
protected final ColumnDefinition column;
protected ColumnSubselection(ColumnDefinition column)
{
this.column = column;
}
public static ColumnSubselection slice(ColumnDefinition column, CellPath from, CellPath to)
{
assert column.isComplex() && column.type instanceof CollectionType;
assert from.size() <= 1 && to.size() <= 1;
return new Slice(column, from, to);
}
public static ColumnSubselection element(ColumnDefinition column, CellPath elt)
{
assert column.isComplex() && column.type instanceof CollectionType;
assert elt.size() == 1;
return new Element(column, elt);
}
public ColumnDefinition column()
{
return column;
}
protected abstract Kind kind();
protected abstract CellPath comparisonPath();
public int compareTo(ColumnSubselection other)
{
assert other.column().name.equals(column().name);
return column().cellPathComparator().compare(comparisonPath(), other.comparisonPath());
}
/**
* Given a path, return -1 if the path is before anything selected by this subselection, 0 if it is selected by this
* subselection and 1 if the path is after anything selected by this subselection.
*/
public abstract int compareInclusionOf(CellPath path);
private static class Slice extends ColumnSubselection
{
private final CellPath from;
private final CellPath to;
private Slice(ColumnDefinition column, CellPath from, CellPath to)
{
super(column);
this.from = from;
this.to = to;
}
protected Kind kind()
{
return Kind.SLICE;
}
public CellPath comparisonPath()
{
return from;
}
public int compareInclusionOf(CellPath path)
{
Comparator<CellPath> cmp = column.cellPathComparator();
if (cmp.compare(path, from) < 0)
return -1;
else if (cmp.compare(to, path) < 0)
return 1;
else
return 0;
}
@Override
public String toString()
{
// This assert we're dealing with a collection since that's the only thing it's used for so far.
AbstractType<?> type = ((CollectionType<?>)column().type).nameComparator();
return String.format("[%s:%s]", from == CellPath.BOTTOM ? "" : type.getString(from.get(0)), to == CellPath.TOP ? "" : type.getString(to.get(0)));
}
}
private static class Element extends ColumnSubselection
{
private final CellPath element;
private Element(ColumnDefinition column, CellPath elt)
{
super(column);
this.element = elt;
}
protected Kind kind()
{
return Kind.ELEMENT;
}
public CellPath comparisonPath()
{
return element;
}
public int compareInclusionOf(CellPath path)
{
return column.cellPathComparator().compare(path, element);
}
@Override
public String toString()
{
// This assert we're dealing with a collection since that's the only thing it's used for so far.
AbstractType<?> type = ((CollectionType<?>)column().type).nameComparator();
return String.format("[%s]", type.getString(element.get(0)));
}
}
public static class Serializer
{
public void serialize(ColumnSubselection subSel, DataOutputPlus out, int version) throws IOException
{
ColumnDefinition column = subSel.column();
ByteBufferUtil.writeWithShortLength(column.name.bytes, out);
out.writeByte(subSel.kind().ordinal());
switch (subSel.kind())
{
case SLICE:
Slice slice = (Slice)subSel;
column.cellPathSerializer().serialize(slice.from, out);
column.cellPathSerializer().serialize(slice.to, out);
break;
case ELEMENT:
Element eltSelection = (Element)subSel;
column.cellPathSerializer().serialize(eltSelection.element, out);
break;
default:
throw new AssertionError();
}
}
public ColumnSubselection deserialize(DataInputPlus in, int version, CFMetaData metadata) throws IOException
{
ByteBuffer name = ByteBufferUtil.readWithShortLength(in);
ColumnDefinition column = metadata.getColumnDefinition(name);
if (column == null)
{
// If we don't find the definition, it could be we have data for a dropped column, and we shouldn't
// fail deserialization because of that. So we grab a "fake" ColumnDefinition that ensure proper
// deserialization. The column will be ignore later on anyway.
column = metadata.getDroppedColumnDefinition(name);
if (column == null)
throw new RuntimeException("Unknown column " + UTF8Type.instance.getString(name) + " during deserialization");
}
Kind kind = Kind.values()[in.readUnsignedByte()];
switch (kind)
{
case SLICE:
CellPath from = column.cellPathSerializer().deserialize(in);
CellPath to = column.cellPathSerializer().deserialize(in);
return new Slice(column, from, to);
case ELEMENT:
CellPath elt = column.cellPathSerializer().deserialize(in);
return new Element(column, elt);
}
throw new AssertionError();
}
public long serializedSize(ColumnSubselection subSel, int version)
{
long size = 0;
ColumnDefinition column = subSel.column();
size += TypeSizes.sizeofWithShortLength(column.name.bytes);
size += 1; // kind
switch (subSel.kind())
{
case SLICE:
Slice slice = (Slice)subSel;
size += column.cellPathSerializer().serializedSize(slice.from);
size += column.cellPathSerializer().serializedSize(slice.to);
break;
case ELEMENT:
Element element = (Element)subSel;
size += column.cellPathSerializer().serializedSize(element.element);
break;
}
return size;
}
}
}