package org.apache.cassandra.db.transform;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.db.DeletionTime;
import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator;
import org.apache.cassandra.db.rows.RangeTombstoneMarker;
import org.apache.cassandra.db.rows.UnfilteredRowIterator;
public final class RTBoundValidator extends Transformation<UnfilteredRowIterator>
{
public enum Stage { MEMTABLE, SSTABLE, MERGED, PURGED, PROCESSED }
private final Stage stage;
private final boolean enforceIsClosed;
private RTBoundValidator(Stage stage, boolean enforceIsClosed)
{
this.stage = stage;
this.enforceIsClosed = enforceIsClosed;
}
public static UnfilteredPartitionIterator validate(UnfilteredPartitionIterator partitions, Stage stage, boolean enforceIsClosed)
{
return Transformation.apply(partitions, new RTBoundValidator(stage, enforceIsClosed));
}
public static UnfilteredRowIterator validate(UnfilteredRowIterator partition, Stage stage, boolean enforceIsClosed)
{
return Transformation.apply(partition, new RowsTransformation(stage, partition.metadata(), partition.isReverseOrder(), enforceIsClosed));
}
@Override
public UnfilteredRowIterator applyToPartition(UnfilteredRowIterator partition)
{
return Transformation.apply(partition, new RowsTransformation(stage, partition.metadata(), partition.isReverseOrder(), enforceIsClosed));
}
private final static class RowsTransformation extends Transformation
{
private final Stage stage;
private final CFMetaData metadata;
private final boolean isReverseOrder;
private final boolean enforceIsClosed;
private DeletionTime openMarkerDeletionTime;
private RowsTransformation(Stage stage, CFMetaData metadata, boolean isReverseOrder, boolean enforceIsClosed)
{
this.stage = stage;
this.metadata = metadata;
this.isReverseOrder = isReverseOrder;
this.enforceIsClosed = enforceIsClosed;
}
@Override
public RangeTombstoneMarker applyToMarker(RangeTombstoneMarker marker)
{
if (null == openMarkerDeletionTime)
{
if (marker.isClose(isReverseOrder))
throw ise("unexpected end bound or boundary " + marker.toString(metadata));
}
else
{
if (!marker.isClose(isReverseOrder))
throw ise("start bound followed by another start bound " + marker.toString(metadata));
DeletionTime deletionTime = marker.closeDeletionTime(isReverseOrder);
if (!deletionTime.equals(openMarkerDeletionTime))
throw ise("open marker and close marker have different deletion times");
openMarkerDeletionTime = null;
}
if (marker.isOpen(isReverseOrder))
openMarkerDeletionTime = marker.openDeletionTime(isReverseOrder);
return marker;
}
@Override
public void onPartitionClose()
{
if (enforceIsClosed && null != openMarkerDeletionTime)
throw ise("expected all RTs to be closed, but the last one is open");
}
private IllegalStateException ise(String why)
{
String message = String.format("%s UnfilteredRowIterator for %s.%s has an illegal RT bounds sequence: %s",
stage, metadata.ksName, metadata.cfName, why);
throw new IllegalStateException(message);
}
}
}