package org.apache.cassandra.db.columniterator; /* * * 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. * */ import java.io.IOError; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import com.google.common.collect.AbstractIterator; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.db.ColumnFamily; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.IColumn; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.io.sstable.IndexHelper; import org.apache.cassandra.io.util.FileDataInput; import org.apache.cassandra.io.util.FileMark; import org.apache.cassandra.io.sstable.SSTableReader; import org.apache.cassandra.io.util.FileUtils; /** * This is a reader that finds the block for a starting column and returns * blocks before/after it for each next call. This function assumes that * the CF is sorted by name and exploits the name index. */ class IndexedSliceReader extends AbstractIterator<IColumn> implements IColumnIterator { private final ColumnFamily emptyColumnFamily; private final List<IndexHelper.IndexInfo> indexes; private final FileDataInput file; private final ByteBuffer startColumn; private final ByteBuffer finishColumn; private final boolean reversed; private BlockFetcher fetcher; private Deque<IColumn> blockColumns = new ArrayDeque<IColumn>(); private AbstractType comparator; public IndexedSliceReader(SSTableReader sstable, FileDataInput input, ByteBuffer startColumn, ByteBuffer finishColumn, boolean reversed) { this.file = input; this.startColumn = startColumn; this.finishColumn = finishColumn; this.reversed = reversed; comparator = sstable.metadata.comparator; try { IndexHelper.skipBloomFilter(file); indexes = IndexHelper.deserializeIndex(file); emptyColumnFamily = ColumnFamily.serializer().deserializeFromSSTableNoColumns(ColumnFamily.create(sstable.metadata), file); fetcher = indexes == null ? new SimpleBlockFetcher() : new IndexedBlockFetcher(); } catch (IOException e) { throw new IOError(e); } } public ColumnFamily getColumnFamily() { return emptyColumnFamily; } public DecoratedKey getKey() { throw new UnsupportedOperationException(); } private boolean isColumnNeeded(IColumn column) { if (startColumn.remaining() == 0 && finishColumn.remaining() == 0) return true; else if (startColumn.remaining() == 0 && !reversed) return comparator.compare(column.name(), finishColumn) <= 0; else if (startColumn.remaining() == 0 && reversed) return comparator.compare(column.name(), finishColumn) >= 0; else if (finishColumn.remaining() == 0 && !reversed) return comparator.compare(column.name(), startColumn) >= 0; else if (finishColumn.remaining() == 0 && reversed) return comparator.compare(column.name(), startColumn) <= 0; else if (!reversed) return comparator.compare(column.name(), startColumn) >= 0 && comparator.compare(column.name(), finishColumn) <= 0; else // if reversed return comparator.compare(column.name(), startColumn) <= 0 && comparator.compare(column.name(), finishColumn) >= 0; } protected IColumn computeNext() { while (true) { IColumn column = blockColumns.poll(); if (column != null && isColumnNeeded(column)) return column; try { if (column == null && !fetcher.getNextBlock()) return endOfData(); } catch (IOException e) { throw new RuntimeException(e); } } } public void close() { } interface BlockFetcher { public boolean getNextBlock() throws IOException; } private class IndexedBlockFetcher implements BlockFetcher { private final FileMark mark; private int curRangeIndex; IndexedBlockFetcher() throws IOException { file.readInt(); // column count this.mark = file.mark(); curRangeIndex = IndexHelper.indexFor(startColumn, indexes, comparator, reversed); } public boolean getNextBlock() throws IOException { if (curRangeIndex < 0 || curRangeIndex >= indexes.size()) return false; /* seek to the correct offset to the data, and calculate the data size */ IndexHelper.IndexInfo curColPosition = indexes.get(curRangeIndex); /* see if this read is really necessary. */ if (reversed) { if ((finishColumn.remaining() > 0 && comparator.compare(finishColumn, curColPosition.lastName) > 0) || (startColumn.remaining() > 0 && comparator.compare(startColumn, curColPosition.firstName) < 0)) return false; } else { if ((startColumn.remaining() > 0 && comparator.compare(startColumn, curColPosition.lastName) > 0) || (finishColumn.remaining() > 0 && comparator.compare(finishColumn, curColPosition.firstName) < 0)) return false; } boolean outOfBounds = false; file.reset(mark); FileUtils.skipBytesFully(file, curColPosition.offset); while (file.bytesPastMark(mark) < curColPosition.offset + curColPosition.width && !outOfBounds) { IColumn column = emptyColumnFamily.getColumnSerializer().deserialize(file); if (reversed) blockColumns.addFirst(column); else blockColumns.addLast(column); /* see if we can stop seeking. */ if (!reversed && finishColumn.remaining() > 0) outOfBounds = comparator.compare(column.name(), finishColumn) >= 0; else if (reversed && startColumn.remaining() > 0) outOfBounds = comparator.compare(column.name(), startColumn) >= 0; } if (reversed) curRangeIndex--; else curRangeIndex++; return true; } } private class SimpleBlockFetcher implements BlockFetcher { private SimpleBlockFetcher() throws IOException { int columns = file.readInt(); for (int i = 0; i < columns; i++) { IColumn column = emptyColumnFamily.getColumnSerializer().deserialize(file); if (reversed) blockColumns.addFirst(column); else blockColumns.addLast(column); /* see if we can stop seeking. */ boolean outOfBounds = false; if (!reversed && finishColumn.remaining() > 0) outOfBounds = comparator.compare(column.name(), finishColumn) >= 0; else if (reversed && startColumn.remaining() > 0) outOfBounds = comparator.compare(column.name(), startColumn) >= 0; if (outOfBounds) break; } } public boolean getNextBlock() throws IOException { return false; } } }