com.bigdata.btree.ChunkedLocalRangeIterator Maven / Gradle / Ivy
/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Feb 1, 2008
*/
package com.bigdata.btree;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Iterator;
import com.bigdata.btree.filter.ITupleFilter;
import com.bigdata.io.ByteBufferInputStream;
import com.bigdata.rawstore.IBlock;
import com.bigdata.rawstore.IRawStore;
import cutthecrap.utils.striterators.IFilter;
/**
* Chunked range iterator running against a local index or index view.
*
* Note: When {@link IRangeQuery#REMOVEALL} is specified without the
* {@link IRangeQuery#CURSOR} flag, the iterator will populate its buffers up to
* the capacity and then delete behind once the buffer is full or as soon as the
* iterator is exhausted. This approach works with the somewhat faster
* {@link ITupleIterator} construct. When the {@link IRangeQuery#CURSOR} flag is
* specified, we directly use the {@link ITupleCursor} interface.
*
* @author Bryan Thompson
* @version $Id$
*/
public class ChunkedLocalRangeIterator extends AbstractChunkedTupleIterator {
/** The backing {@link IIndex}. */
protected final IIndex ndx;
/** Iff {@link IRangeQuery#REMOVEALL} was specified. */
private final boolean removeAll;
/** Iff {@link IRangeQuery#CURSOR} was specified. */
private final boolean cursor;
/**
* @param fromKey
* @param toKey
* @param capacity
* @param flags
* @param filter
*/
public ChunkedLocalRangeIterator(IIndex ndx, byte[] fromKey, byte[] toKey,
int capacity, int flags, IFilter filter) {
super(fromKey, toKey, capacity, flags, filter);
if (ndx == null)
throw new IllegalArgumentException();
this.ndx = ndx;
removeAll = (flags & IRangeQuery.REMOVEALL) != 0;
cursor = (flags & IRangeQuery.CURSOR) != 0;
}
/**
* Note: timestamp is ignored since we are reading against a local
* index object.
*/
@Override
protected ResultSet getResultSet(final long timestamp,
final byte[] fromKey, final byte[] toKey, final int capacity,
final int flags, final IFilter filter) {
/*
* Note: This turns off the REMOVEALL flag unless CURSOR was also
* specified.
*/
final int tmpFlags = (removeAll && !cursor) //
? flags & ~IRangeQuery.REMOVEALL //
: flags;
/*
* Figure out the upper bound on the #of tuples that could be
* materialized.
*
* Note: the upper bound on the #of key-value pairs in the range is
* Integer.MAX_VALUE.
*
* @todo Shouldn't the upper bound be something a little less large,
* such as 100,000? If the upper bound is in fact the limit on the #of
* tuples to be buffered at a time, then Integer.MAX_VALUE is simply too
* large.
*/
final int rangeCount = (int) Math.min((long) Integer.MAX_VALUE, ndx
.rangeCount(fromKey, toKey));
final int limit = (rangeCount > capacity ? capacity : rangeCount);
/*
* Iterator that will visit the key range.
*
* Note: We always visit the keys regardless of whether we pass them
* on to the caller. This is necessary in order for us to set the
* [lastKey] field on the result set and that is necessary to
* support continuation queries.
*/
final ITupleIterator itr = ndx.rangeIterator(fromKey, toKey, limit,
tmpFlags | IRangeQuery.KEYS, filter);
/*
* @todo shouldn't we pass [flags] (not [tmpFlags]) into the ctor? we
* don't need to materialize the keys to the remote caller, just so we
* can get the [lastKey].
*/
return new ResultSet(ndx, limit, tmpFlags, itr);
}
/**
* Visits the next tuple, queuing it for removal unless
* {@link IRangeQuery#CURSOR} was specified, in which case it is deleted
* immediately.
*
* Note: Queuing for removal is done only for the local index so that data
* service range iterators will do their deletes on the local index when
* this range iterator runs rather than buffering the keys and then sending
* back a batch delete to the index later (this would also make
* {@link IRangeQuery#REMOVEALL} non-atomic).
*/
public ITuple next() {
final ITuple tuple = super.next();
if (removeAll) {
if (!cursor) {
/*
* Queue the key for removal IFF CURSOR was NOT specified.
*/
remove();
}
}
return tuple;
}
@Override
protected void deleteBehind(int n, Iterator keys) {
while(keys.hasNext()) {
ndx.remove(keys.next());
}
}
@Override
protected void deleteLast(byte[] key) {
ndx.remove(key);
}
@Override
protected IBlock readBlock(final int sourceIndex, final long addr) {
if(ndx instanceof AbstractBTree ) {
/*
* A local B+Tree.
*/
if (sourceIndex != 0) {
// Since this is not a view the source index MUST be zero.
throw new IllegalArgumentException();
}
return new IBlock() {
public long getAddress() {
return addr;
}
public InputStream inputStream() {
final IRawStore store = ((AbstractBTree)ndx).getStore();
final ByteBuffer buf = store.read(addr);
return new ByteBufferInputStream(buf);
}
public int length() {
final IRawStore store = ((AbstractBTree)ndx).getStore();
return store.getByteCount(addr);
}
};
} else {
/*
* @todo A view onto two or more B+Trees. Some of these can be index
* segments. Each index segment is in its own file store, so we have
* to direct the request to the correct backing store. Make sure
* that we do not double-open a index segment file store.
*/
// using source index, read record from the identifed source.
throw new UnsupportedOperationException();
}
}
/**
* Always returns 0L. This value is ignored by
* {@link #getResultSet(long, byte[], byte[], int, int, ITupleFilter)}.
*/
@Override
protected long getTimestamp() {
return 0L;
}
/**
* Returns true
since the read will be consistent (it reads
* against the same index object for each {@link ResultSet}) but the values
* returned by {@link #getTimestamp()} and {@link #getReadTime()} are
* ignored by
* {@link #getResultSet(long, byte[], byte[], int, int, ITupleFilter)}
* (since it is reading against a local index object).
*/
@Override
public boolean getReadConsistent() {
return false;
}
}