blob: 0d5acd177d0a4048953061e975246bf2fd027093 [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 org.apache.cassandra.db.Cell;
import org.apache.cassandra.db.composites.CellName;
import org.apache.cassandra.db.composites.CellNameType;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.DeletionInfo;
public class ColumnCounter
{
protected int live;
protected int tombstones;
protected final long timestamp;
public ColumnCounter(long timestamp)
{
this.timestamp = timestamp;
}
/**
* @return true if the cell counted as a live cell or a valid tombstone; false if it got immediately discarded for
* being shadowed by a range- or a partition tombstone
*/
public boolean count(Cell cell, DeletionInfo.InOrderTester tester)
{
// The cell is shadowed by a higher-level deletion, and won't be retained.
// For the purposes of this counter, we don't care if it's a tombstone or not.
if (tester.isDeleted(cell))
return false;
if (cell.isLive(timestamp))
live++;
else
tombstones++;
return true;
}
public int live()
{
return live;
}
public int tombstones()
{
return tombstones;
}
public ColumnCounter countAll(ColumnFamily container)
{
if (container == null)
return this;
DeletionInfo.InOrderTester tester = container.inOrderDeletionTester();
for (Cell c : container)
count(c, tester);
return this;
}
public static class GroupByPrefix extends ColumnCounter
{
protected final CellNameType type;
protected final int toGroup;
protected CellName previous;
/**
* A column counter that count only 1 for all the columns sharing a
* given prefix of the key.
*
* @param type the type of the column name. This can be null if {@code
* toGroup} is 0, otherwise it should be a composite.
* @param toGroup the number of composite components on which to group
* column. If 0, all columns are grouped, otherwise we group
* those for which the {@code toGroup} first component are equals.
*/
public GroupByPrefix(long timestamp, CellNameType type, int toGroup)
{
super(timestamp);
this.type = type;
this.toGroup = toGroup;
assert toGroup == 0 || type != null;
}
@Override
public boolean count(Cell cell, DeletionInfo.InOrderTester tester)
{
if (tester.isDeleted(cell))
return false;
if (!cell.isLive(timestamp))
{
tombstones++;
return true;
}
if (toGroup == 0)
{
live = 1;
return true;
}
CellName current = cell.name();
assert current.size() >= toGroup;
if (previous != null)
{
boolean isSameGroup = previous.isStatic() == current.isStatic();
if (isSameGroup)
{
for (int i = 0; i < toGroup; i++)
{
if (type.subtype(i).compare(previous.get(i), current.get(i)) != 0)
{
isSameGroup = false;
break;
}
}
}
if (isSameGroup)
return true;
// We want to count the static group as 1 (CQL) row only if it's the only
// group in the partition. So, since we have already counted it at this point,
// just don't count the 2nd group if there is one and the first one was static
if (previous.isStatic())
{
previous = current;
return true;
}
}
live++;
previous = current;
return true;
}
}
/**
* Similar to GroupByPrefix, but designed to handle counting cells in reverse order.
*/
public static class GroupByPrefixReversed extends GroupByPrefix
{
public GroupByPrefixReversed(long timestamp, CellNameType type, int toGroup)
{
super(timestamp, type, toGroup);
}
@Override
public boolean count(Cell cell, DeletionInfo.InOrderTester tester)
{
if (tester.isDeleted(cell))
return false;
if (!cell.isLive(timestamp))
{
tombstones++;
return true;
}
if (toGroup == 0)
{
live = 1;
return true;
}
CellName current = cell.name();
assert current.size() >= toGroup;
if (previous == null)
{
// This is the first group we've seen. If it happens to be static, we still want to increment the
// count because a) there are no-static rows (statics are always last in reversed order), and b) any
// static cells we see after this will not increment the count
previous = current;
live++;
}
else if (!current.isStatic()) // ignore statics if we've seen any other statics or any other groups
{
for (int i = 0; i < toGroup; i++)
{
if (type.subtype(i).compare(previous.get(i), current.get(i)) != 0)
{
// it's a new group
live++;
previous = current;
return true;
}
}
}
return true;
}
}
}