import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;
}
/**
- * Restores the current position to the previous position.
+ * Restores a current position for the cursor (current position becomes
+ * previous position).
*/
protected void restorePosition(Position curPos)
throws IOException
{
- if(!curPos.equals(_curPos)) {
+ restorePosition(curPos, _curPos);
+ }
+
+ /**
+ * Restores a current and previous position for the cursor.
+ */
+ protected void restorePosition(Position curPos, Position prevPos)
+ throws IOException
+ {
+ if(!curPos.equals(_curPos) || !prevPos.equals(_prevPos)) {
// make the current position previous, and the new position current
_prevPos = _curPos;
_curPos = curPos;
throws IOException
{
Position curPos = _curPos;
+ Position prevPos = _prevPos;
boolean found = false;
try {
found = findRowImpl(columnPattern, valuePattern);
} finally {
if(!found) {
try {
- restorePosition(curPos);
+ restorePosition(curPos, prevPos);
} catch(IOException e) {
LOG.error("Failed restoring position", e);
}
throws IOException
{
Position curPos = _curPos;
+ Position prevPos = _prevPos;
boolean found = false;
try {
found = findRowImpl(rowPattern);
} finally {
if(!found) {
try {
- restorePosition(curPos);
+ restorePosition(curPos, prevPos);
} catch(IOException e) {
LOG.error("Failed restoring position", e);
}
/**
* Returns the given column from the current row.
*/
- @SuppressWarnings("foo")
public Object getCurrentRowValue(Column column)
throws IOException
{
}
@Override
- protected void restorePosition(Position curPos)
+ protected void restorePosition(Position curPos, Position prevPos)
throws IOException
{
- if(!(curPos instanceof ScanPosition)) {
+ if(!(curPos instanceof ScanPosition) ||
+ !(prevPos instanceof ScanPosition)) {
throw new IllegalArgumentException(
- "New position must be a scan position");
+ "Restored positions must be scan positions");
}
- super.restorePosition(curPos);
- _ownedPagesCursor.setCurrentPage(curPos.getRowId().getPageNumber());
+ super.restorePosition(curPos, prevPos);
+ _ownedPagesCursor.restorePosition(curPos.getRowId().getPageNumber(),
+ prevPos.getRowId().getPageNumber());
}
- /**
- * Position the buffer at the next row in the table
- * @return a ByteBuffer narrowed to the next row, or null if none
- */
@Override
protected Position findAnotherPosition(RowState rowState, Position curPos,
boolean moveForward)
}
@Override
- protected void restorePosition(Position curPos)
+ protected void restorePosition(Position curPos, Position prevPos)
throws IOException
{
- if(!(curPos instanceof IndexPosition)) {
+ if(!(curPos instanceof IndexPosition) ||
+ !(prevPos instanceof IndexPosition)) {
throw new IllegalArgumentException(
- "New position must be an index position");
+ "Restored positions must be index positions");
+ }
+ super.restorePosition(curPos, prevPos);
+ _entryCursor.restorePosition(((IndexPosition)curPos).getEntry(),
+ ((IndexPosition)prevPos).getEntry());
+ }
+
+ @Override
+ protected boolean findRowImpl(Column columnPattern, Object valuePattern)
+ throws IOException
+ {
+ Object[] rowValues = _entryCursor.getIndex().constructIndexRow(
+ columnPattern.getName(), valuePattern);
+
+ if(rowValues == null) {
+ // bummer, use the default table scan
+ return super.findRowImpl(columnPattern, valuePattern);
+ }
+
+ // sweet, we can use our index
+ _entryCursor.beforeEntry(rowValues);
+ Index.Entry startEntry = _entryCursor.getNextEntry();
+ if(!startEntry.getRowId().isValid()) {
+ // at end of index, no potential matches
+ return false;
+ }
+
+ // either we found a row with the given value, or none exist in the
+ // table
+ restorePosition(new IndexPosition(startEntry));
+ return ObjectUtils.equals(getCurrentRowValue(columnPattern),
+ valuePattern);
+ }
+
+ @Override
+ protected boolean findRowImpl(Map<String,Object> rowPattern)
+ throws IOException
+ {
+ Index index = _entryCursor.getIndex();
+ Object[] rowValues = index.constructIndexRow(rowPattern);
+
+ if(rowValues == null) {
+ // bummer, use the default table scan
+ return super.findRowImpl(rowPattern);
+ }
+
+ // sweet, we can use our index
+ _entryCursor.beforeEntry(rowValues);
+ Index.Entry startEntry = _entryCursor.getNextEntry();
+ if(!startEntry.getRowId().isValid()) {
+ // at end of index, no potential matches
+ return false;
+ }
+ restorePosition(new IndexPosition(startEntry));
+
+ Map<String,Object> indexRowPattern =
+ new LinkedHashMap<String,Object>();
+ for(Column idxCol : _entryCursor.getIndex().getColumns()) {
+ indexRowPattern.put(idxCol.getName(),
+ rowValues[idxCol.getColumnNumber()]);
}
- super.restorePosition(curPos);
- _entryCursor.setCurrentEntry(((IndexPosition)curPos).getEntry());
+
+ // there may be multiple columns which fit the pattern subset used by
+ // the index, so we need to keep checking until we no longer our index
+ // values no longer match
+ do {
+
+ if(!ObjectUtils.equals(getCurrentRow(indexRowPattern.keySet()),
+ indexRowPattern)) {
+ // there are no more rows which could possibly match
+ break;
+ }
+
+ if(ObjectUtils.equals(getCurrentRow(rowPattern.keySet()),
+ rowPattern)) {
+ // found it!
+ return true;
+ }
+
+ } while(moveToNextRow());
+
+ // none of the potential rows matched
+ return false;
}
- /**
- * Position the buffer at the next row in the table
- * @return a ByteBuffer narrowed to the next row, or null if none
- */
@Override
protected Position findAnotherPosition(RowState rowState, Position curPos,
boolean moveForward)
@Override
public final boolean equals(Object o) {
return((this == o) ||
- ((getClass() == o.getClass()) && equalsImpl(o)));
+ ((o != null) && (getClass() == o.getClass()) && equalsImpl(o)));
}
/**
return removed;
}
+
+ /**
+ * Constructs an array of values appropriate for this index from the given
+ * column values, expected to match the columns for this index.
+ * @return the appropriate sparse array of data
+ * @throws IllegalArgumentException if the wrong number of values are
+ * provided
+ */
+ public Object[] constructIndexRow(Object... values)
+ {
+ if(values.length != _columns.size()) {
+ throw new IllegalArgumentException(
+ "Wrong number of column values given " + values.length +
+ ", expected " + _columns.size());
+ }
+ int valIdx = 0;
+ Object[] idxRow = new Object[getTable().getMaxColumnCount()];
+ for(Column col : _columns.keySet()) {
+ idxRow[col.getColumnNumber()] = values[valIdx++];
+ }
+ return idxRow;
+ }
+
+ /**
+ * Constructs an array of values appropriate for this index from the given
+ * column value.
+ * @return the appropriate sparse array of data or {@code null} if not all
+ * columns for this index were provided
+ */
+ public Object[] constructIndexRow(String colName, Object value)
+ {
+ return constructIndexRow(Collections.singletonMap(colName, value));
+ }
+ /**
+ * Constructs an array of values appropriate for this index from the given
+ * column values.
+ * @return the appropriate sparse array of data or {@code null} if not all
+ * columns for this index were provided
+ */
+ public Object[] constructIndexRow(Map<String,Object> row)
+ {
+ for(Column col : _columns.keySet()) {
+ if(!row.containsKey(col.getName())) {
+ return null;
+ }
+ }
+
+ Object[] idxRow = new Object[getTable().getMaxColumnCount()];
+ for(Column col : _columns.keySet()) {
+ idxRow[col.getColumnNumber()] = row.get(col.getName());
+ }
+ return idxRow;
+ }
@Override
public String toString() {
}
public boolean isValid() {
- return _rowId.isValid();
+ return(_entryColumns != null);
}
/**
@Override
public boolean equals(Object o) {
return((this == o) ||
- ((getClass() == o.getClass()) &&
+ ((o != null) && (getClass() == o.getClass()) &&
(compareTo((Entry)o) == 0)));
}
return 0;
}
- // note, if the one or both of the entries has no entryColumns, it is a
- // "special" entry which should be compared on the rowId alone
- if((_entryColumns != null) && (other.getEntryColumns() != null)) {
+ // note, if the one or both of the entries are not valid, they are
+ // "special" entries, which are handled below
+ if(isValid() && other.isValid()) {
+
+ // comparing two normal entries
Iterator<EntryColumn> myIter = _entryColumns.iterator();
Iterator<EntryColumn> otherIter = other.getEntryColumns().iterator();
while (myIter.hasNext()) {
return i;
}
}
+
+ // if entry columns are equal, sort by rowIds
+ return _rowId.compareTo(other.getRowId());
+ }
+
+ // this is the odd case where mixed entries are being compared. if both
+ // entries are invalid or the rowIds are not equal, then use the rowId
+ // comparison.
+ int rowCmp = _rowId.compareTo(other.getRowId());
+ if((isValid() == other.isValid()) || (rowCmp != 0)) {
+ return rowCmp;
}
- return _rowId.compareTo(other.getRowId());
+
+ // at this point, the rowId's are equal, but the validity is not. this
+ // will happen when a "special" entry is compared to something created
+ // by EntryCursor.afterEntry or EntryCursor.beforeEntry. in this case,
+ // the FIRST_ENTRY is always least and the LAST_ENTRY is always
+ // greatest.
+ int cmp = 0;
+ Entry invalid = null;
+ if(!isValid()) {
+ cmp = -1;
+ invalid = this;
+ } else {
+ cmp = 1;
+ invalid = other;
+ }
+ return (cmp * (invalid.equals(FIRST_ENTRY.getRowId()) ? 1 : -1));
}
}
}
+ @Override
+ public boolean equals(Object o) {
+ return((this == o) ||
+ ((o != null) && (o != null) && (getClass() == o.getClass()) &&
+ (compareTo((EntryColumn)o) == 0)));
+ }
+
/**
* Write this non-null entry column to a buffer
*/
reset();
}
+ public Index getIndex() {
+ return Index.this;
+ }
+
/**
* Returns the DirHandler for the given direction
*/
public void beforeEntry(Object[] row)
throws IOException
{
- // FIXME, change how row is given?
restorePosition(new Entry(row, RowId.FIRST_ROW_ID, _columns));
}
public void afterEntry(Object[] row)
throws IOException
{
- // FIXME, change how row is given?
restorePosition(new Entry(row, RowId.LAST_ROW_ID, _columns));
}
-
- /**
- * Returns the current entry.
- */
- public Entry getCurrentEntry() {
- return _curPos.getEntry();
- }
-
- /**
- * Resets the cursor to the given entry.
- */
- public void setCurrentEntry(Entry entry) {
- restorePosition(entry);
- }
/**
* @return valid entry if there was entry, {@link Index#LAST_ENTRY}
}
/**
- * Restores a previous position for the cursor.
+ * Restores a current position for the cursor (current position becomes
+ * previous position).
*/
- private void restorePosition(Entry curEntry)
+ private void restorePosition(Entry curEntry) {
+ restorePosition(curEntry, _curPos.getEntry());
+ }
+
+ /**
+ * Restores a current and previous position for the cursor.
+ */
+ protected void restorePosition(Entry curEntry, Entry prevEntry)
{
- if(!curEntry.equals(_curPos.getEntry())) {
- _prevPos = updatePosition(_curPos.getEntry());
+ if(!curEntry.equals(_curPos.getEntry()) ||
+ !prevEntry.equals(_prevPos.getEntry()))
+ {
+ _prevPos = updatePosition(prevEntry);
_curPos = updatePosition(curEntry);
_lastModCount = Index.this._modCount;
} else {
*/
private Position updatePosition(Entry entry) {
int curIdx = FIRST_ENTRY_IDX;
- boolean deleted = false;
+ boolean between = false;
RowId rowId = entry.getRowId();
- if(rowId.isValid()) {
+ if(entry.isValid()) {
// find the new position for this entry
int idx = findEntry(entry);
if(idx >= 0) {
curIdx = idx;
} else {
- // given entry was deleted. our current position is now really
- // between two indexes, but we cannot support that as an integer
- // value so we set a flag instead
+ // given entry was not found exactly. our current position is now
+ // really between two indexes, but we cannot support that as an
+ // integer value so we set a flag instead
curIdx = missingIndexToInsertionPoint(idx);
- deleted = true;
+ between = true;
}
} else if(rowId.equals(RowId.FIRST_ROW_ID)) {
curIdx = FIRST_ENTRY_IDX;
} else {
throw new IllegalArgumentException("Invalid entry given: " + entry);
}
- return new Position(curIdx, entry, deleted);
+ return new Position(curIdx, entry, between);
}
/**
_prevPos = _curPos;
_curPos = handler.getAnotherPosition(_curPos.getIndex(),
- _curPos.isDeleted());
+ _curPos.isBetween());
return _curPos.getEntry();
}
* logic from value storage.
*/
private abstract class DirHandler {
- public abstract Position getAnotherPosition(int curIdx, boolean deleted);
+ public abstract Position getAnotherPosition(int curIdx, boolean between);
public abstract Position getBeginningPosition();
public abstract Position getEndPosition();
protected final Position newPosition(int curIdx) {
*/
private final class ForwardDirHandler extends DirHandler {
@Override
- public Position getAnotherPosition(int curIdx, boolean deleted) {
+ public Position getAnotherPosition(int curIdx, boolean between) {
// note, curIdx does not need to be advanced if it was pointing at a
- // deleted entry
- if(!deleted) {
+ // between position
+ if(!between) {
curIdx = ((curIdx == getBeginningPosition().getIndex()) ?
0 : (curIdx + 1));
}
*/
private final class ReverseDirHandler extends DirHandler {
@Override
- public Position getAnotherPosition(int curIdx, boolean deleted) {
- // note, we ignore the deleted flag here because the index will be
- // pointing at the correct next index in either the deleted or
- // non-deleted case
+ public Position getAnotherPosition(int curIdx, boolean between) {
+ // note, we ignore the between flag here because the index will be
+ // pointing at the correct next index in either the between or
+ // non-between case
curIdx = ((curIdx == getBeginningPosition().getIndex()) ?
(_entries.size() - 1) : (curIdx - 1));
return newReversePosition(curIdx);
private final Entry _entry;
/** {@code true} if this entry does not currently exist in the entry list,
{@code false} otherwise */
- private final boolean _deleted;
+ private final boolean _between;
private Position(int idx, Entry entry) {
this(idx, entry, false);
}
- private Position(int idx, Entry entry, boolean deleted) {
+ private Position(int idx, Entry entry, boolean between) {
_idx = idx;
_entry = entry;
- _deleted = deleted;
+ _between = between;
}
public int getIndex() {
return _entry;
}
- public boolean isDeleted() {
- return _deleted;
+ public boolean isBetween() {
+ return _between;
}
@Override
public boolean equals(Object o) {
return((this == o) ||
- ((getClass() == o.getClass()) &&
+ ((o != null) && (getClass() == o.getClass()) &&
(_idx == ((Position)o)._idx) &&
_entry.equals(((Position)o)._entry) &&
- (_deleted == ((Position)o)._deleted)));
+ (_between == ((Position)o)._between)));
}
@Override
public String toString() {
- return "Idx = " + _idx + ", Entry = " + _entry + ", Deleted = " +
- _deleted;
+ return "Idx = " + _idx + ", Entry = " + _entry + ", Between = " +
+ _between;
}
}
return _name;
}
+ public int getMaxColumnCount() {
+ return _maxColumnCount;
+ }
+
public Database getDatabase() {
return _database;
}
private RowState(boolean hardRowBuffer) {
_headerRowBufferH = TempPageHolder.newHolder(hardRowBuffer);
- _rowValues = new Object[Table.this._maxColumnCount];
+ _rowValues = new Object[Table.this.getMaxColumnCount()];
_lastModCount = Table.this._modCount;
}
reset();
}
+ public UsageMap getUsageMap() {
+ return UsageMap.this;
+ }
+
/**
* Returns the DirHandler for the given direction
*/
return(UsageMap.this._modCount == _lastModCount);
}
- /**
- * Returns the current page number.
- */
- public int getCurrentPage() {
- return _curPageNumber;
- }
-
- /**
- * Resets the cursor to the given page number.
- */
- public void setCurrentPage(int curPageNumber) {
- if(curPageNumber < UsageMap.this.getFirstPageNumber()) {
- curPageNumber = RowId.FIRST_PAGE_NUMBER;
- } else if(curPageNumber > UsageMap.this.getLastPageNumber()) {
- curPageNumber = RowId.LAST_PAGE_NUMBER;
- }
- restorePosition(curPageNumber);
- }
-
/**
* @return valid page number if there was another page to read,
* {@link RowId#LAST_PAGE_NUMBER} otherwise
}
/**
- * Restores a previous position for the cursor.
+ * Restores a current position for the cursor (current position becomes
+ * previous position).
*/
- private void restorePosition(int curPageNumber) {
- if(curPageNumber != _curPageNumber) {
- _prevPageNumber = _curPageNumber;
- _curPageNumber = curPageNumber;
+ private void restorePosition(int curPageNumber)
+ {
+ restorePosition(curPageNumber, _curPageNumber);
+ }
+
+ /**
+ * Restores a current and previous position for the cursor.
+ */
+ protected void restorePosition(int curPageNumber, int prevPageNumber) {
+ if((curPageNumber != _curPageNumber) ||
+ (prevPageNumber != _prevPageNumber))
+ {
+ _prevPageNumber = updatePosition(prevPageNumber);
+ _curPageNumber = updatePosition(curPageNumber);
+ _lastModCount = UsageMap.this._modCount;
+ } else {
+ checkForModification();
}
- _lastModCount = UsageMap.this._modCount;
}
/**
* Checks the usage map for modifications an updates state accordingly.
*/
private void checkForModification() {
- // since page numbers are not affected by modifications, we don't need
- // to adjust anything
- _lastModCount = UsageMap.this._modCount;
+ if(!isUpToDate()) {
+ _prevPageNumber = updatePosition(_prevPageNumber);
+ _curPageNumber = updatePosition(_curPageNumber);
+ _lastModCount = UsageMap.this._modCount;
+ }
}
+ private int updatePosition(int pageNumber) {
+ if(pageNumber < UsageMap.this.getFirstPageNumber()) {
+ pageNumber = RowId.FIRST_PAGE_NUMBER;
+ } else if(pageNumber > UsageMap.this.getLastPageNumber()) {
+ pageNumber = RowId.LAST_PAGE_NUMBER;
+ }
+ return pageNumber;
+ }
+
@Override
public String toString() {
return getClass().getSimpleName() + " CurPosition " + _curPageNumber +
package com.healthmarketscience.jackcess;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
db.createTable("test", columns);
Table table = db.getTable("test");
- for(int i = 0; i < 10; ++i) {
- table.addRow(i, "data" + i);
+ for(Map<String,Object> row : createTestTableData()) {
+ table.addRow(row.get("id"), row.get("value"));
}
return db;
}
+ private static List<Map<String,Object>> createUnorderedTestTableData()
+ throws Exception
+ {
+ List<Map<String,Object>> expectedRows =
+ new ArrayList<Map<String,Object>>();
+ int[] ids = new int[]{3, 7, 6, 1, 2, 9, 0, 5, 4, 8};
+ for(int i : ids) {
+ expectedRows.add(createExpectedRow("id", i, "value", "data" + i));
+ }
+ return expectedRows;
+ }
+
+ private static Database createTestIndexTable() throws Exception {
+ File srcFile = new File("test/data/indexCursorTest.mdb");
+ File dbFile = File.createTempFile("databaseTest", ".mdb");
+ dbFile.deleteOnExit();
+ copyFile(srcFile, dbFile);
+
+ Database db = Database.open(dbFile);
+
+ Table table = db.getTable("test");
+
+ for(Map<String,Object> row : createUnorderedTestTableData()) {
+ table.addRow(row.get("id"), row.get("value"));
+ }
+
+ return db;
+ }
+
public void testRowId() throws Exception {
// test special cases
RowId rowId1 = new RowId(1, 2);
Database db = createTestTable();
Table table = db.getTable("test");
+ Cursor cursor = Cursor.createCursor(table);
+ doTestSimple(table, cursor);
+ db.close();
+ }
+
+ private void doTestSimple(Table table, Cursor cursor) throws Exception {
List<Map<String,Object>> expectedRows = createTestTableData();
- Cursor cursor = Cursor.createCursor(table);
List<Map<String, Object>> foundRows =
new ArrayList<Map<String, Object>>();
for(Map<String, Object> row : cursor) {
foundRows.add(row);
}
assertEquals(expectedRows, foundRows);
-
- db.close();
}
public void testSkip() throws Exception {
Database db = createTestTable();
Table table = db.getTable("test");
+ Cursor cursor = Cursor.createCursor(table);
+ doTestSkip(table, cursor);
+
+ db.close();
+ }
+
+ private void doTestSkip(Table table, Cursor cursor) throws Exception {
List<Map<String,Object>> expectedRows = createTestTableData();
expectedRows.subList(1, 4).clear();
- Cursor cursor = Cursor.createCursor(table);
List<Map<String, Object>> foundRows =
new ArrayList<Map<String, Object>>();
foundRows.add(cursor.getNextRow());
assertEquals(expectedRows, foundRows);
assertEquals(0, cursor.skipNextRows(3));
-
- db.close();
}
public void testSearch() throws Exception {
Database db = createTestTable();
Table table = db.getTable("test");
- List<Map<String,Object>> expectedRows = createTestTableData();
-
Cursor cursor = Cursor.createCursor(table);
+ doTestSearch(table, cursor);
+
+ db.close();
+ }
+
+ private void doTestSearch(Table table, Cursor cursor) throws Exception {
+ List<Map<String,Object>> expectedRows = createTestTableData();
assertTrue(cursor.findRow(table.getColumn("id"), 3));
assertEquals(createExpectedRow("id", 3,
Cursor.findValue(table,
table.getColumn("value"),
table.getColumn("id"), 9));
-
- db.close();
}
public void testReverse() throws Exception {
Database db = createTestTable();
Table table = db.getTable("test");
+ Cursor cursor = Cursor.createCursor(table);
+ doTestReverse(table, cursor);
+
+ db.close();
+ }
+
+ private void doTestReverse(Table table, Cursor cursor) throws Exception {
List<Map<String,Object>> expectedRows = createTestTableData();
Collections.reverse(expectedRows);
- Cursor cursor = Cursor.createCursor(table);
List<Map<String, Object>> foundRows =
new ArrayList<Map<String, Object>>();
for(Map<String, Object> row : cursor.reverseIterable()) {
foundRows.add(row);
}
- assertEquals(expectedRows, foundRows);
-
- db.close();
+ assertEquals(expectedRows, foundRows);
}
-
+
public void testLiveAddition() throws Exception {
Database db = createTestTable();
Cursor cursor1 = Cursor.createCursor(table);
Cursor cursor2 = Cursor.createCursor(table);
+ doTestLiveAddition(table, cursor1, cursor2);
+
+ db.close();
+ }
+
+ private void doTestLiveAddition(Table table,
+ Cursor cursor1,
+ Cursor cursor2) throws Exception
+ {
cursor1.skipNextRows(11);
cursor2.skipNextRows(11);
assertFalse(cursor2.moveToNextRow());
assertTrue(cursor1.isAfterLast());
assertTrue(cursor2.isAfterLast());
-
- db.close();
}
+
public void testLiveDeletion() throws Exception {
Database db = createTestTable();
Cursor cursor2 = Cursor.createCursor(table);
Cursor cursor3 = Cursor.createCursor(table);
Cursor cursor4 = Cursor.createCursor(table);
+ doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4);
+
+ db.close();
+ }
+
+ private void doTestLiveDeletion(Table table,
+ Cursor cursor1,
+ Cursor cursor2,
+ Cursor cursor3,
+ Cursor cursor4) throws Exception
+ {
cursor1.skipNextRows(2);
cursor2.skipNextRows(3);
cursor3.skipNextRows(3);
assertEquals(expectedNextRow, cursor3.getNextRow());
assertEquals(expectedPrevRow, cursor3.getPreviousRow());
+ }
+
+ public void testSimpleIndex() throws Exception {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ assertTable(createUnorderedTestTableData(), table);
+
+ Cursor cursor = Cursor.createIndexCursor(table, idx);
+ doTestSimple(table, cursor);
+
+ db.close();
+ }
+
+ public void testSkipIndex() throws Exception {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+ Cursor cursor = Cursor.createIndexCursor(table, idx);
+ doTestSkip(table, cursor);
db.close();
}
+
+ public void testReverseIndex() throws Exception {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+ Cursor cursor = Cursor.createIndexCursor(table, idx);
+ doTestReverse(table, cursor);
+
+ db.close();
+ }
+
+ public void testSearchIndex() throws Exception {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+ Cursor cursor = Cursor.createIndexCursor(table, idx);
+ doTestSearch(table, cursor);
+
+ db.close();
+ }
+
+ public void testLiveAdditionIndex() throws Exception {
+ Database db = createTestIndexTable();
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ Cursor cursor1 = Cursor.createIndexCursor(table, idx);
+ Cursor cursor2 = Cursor.createIndexCursor(table, idx);
+ doTestLiveAddition(table, cursor1, cursor2);
+
+ db.close();
+ }
+
+ public void testLiveDeletionIndex() throws Exception {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ Cursor cursor1 = Cursor.createIndexCursor(table, idx);
+ Cursor cursor2 = Cursor.createIndexCursor(table, idx);
+ Cursor cursor3 = Cursor.createIndexCursor(table, idx);
+ Cursor cursor4 = Cursor.createIndexCursor(table, idx);
+ doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4);
+
+ db.close();
+ }
}