/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.test.it.qp;
import com.foundationdb.qp.expression.IndexBound;
import com.foundationdb.qp.expression.IndexKeyRange;
import com.foundationdb.qp.operator.API;
import com.foundationdb.qp.operator.Cursor;
import com.foundationdb.qp.operator.Operator;
import com.foundationdb.qp.row.Row;
import com.foundationdb.server.api.dml.SetColumnSelector;
import com.foundationdb.server.test.ExpressionGenerators;
import com.foundationdb.server.types.value.ValueSource;
import org.junit.Ignore;
import org.junit.Test;
import static com.foundationdb.qp.operator.API.cursor;
import static com.foundationdb.qp.operator.API.indexScan_Default;
import static com.foundationdb.server.test.ExpressionGenerators.field;
import static org.junit.Assert.assertEquals;
public class IndexScanIT extends OperatorITBase
{
@Override
protected void setupPostCreateSchema() {
super.setupPostCreateSchema();
use(db);
}
// IndexKeyRange argument checking
@Test(expected = IllegalArgumentException.class)
public void testNullLoBound()
{
IndexKeyRange.bounded(itemIidIndexRowType, null, false, iidBound(0), false);
}
@Test(expected = IllegalArgumentException.class)
public void testNullHiBound()
{
IndexKeyRange.bounded(itemOidIidIndexRowType, iidBound(0), false, null, false);
}
@Test(expected = IllegalArgumentException.class)
public void testNullLoAndHiSelectorsMismatch()
{
IndexBound loBound = new IndexBound(row(itemOidIidIndexRowType, 0, 0), new SetColumnSelector(0));
IndexBound hiBound = new IndexBound(row(itemOidIidIndexRowType, 0, 0), new SetColumnSelector(1));
IndexKeyRange.bounded(itemOidIidIndexRowType, loBound, false, hiBound, false);
}
@Test(expected = IllegalArgumentException.class)
public void testSelectorForNonLeadingColumn()
{
IndexBound loBound = new IndexBound(row(itemOidIidIndexRowType, 0, 0), new SetColumnSelector(1));
IndexBound hiBound = new IndexBound(row(itemOidIidIndexRowType, 0, 0), new SetColumnSelector(1));
IndexKeyRange.bounded(itemOidIidIndexRowType, loBound, false, hiBound, false);
}
@Test
public void testLegalSelector()
{
IndexBound loBound = new IndexBound(row(itemOidIidIndexRowType, 0, 0), new SetColumnSelector(0));
IndexBound hiBound = new IndexBound(row(itemOidIidIndexRowType, 0, 0), new SetColumnSelector(0));
IndexKeyRange.bounded(itemOidIidIndexRowType, loBound, false, hiBound, false);
}
// Check invalid ranges (can only be done when cursor is opened)
@Test(expected = IllegalArgumentException.class)
public void testMoreThanOneInequalityUnidirectional()
{
IndexBound loBound = new IndexBound(row(itemOidIidIndexRowType, 10, 10), new SetColumnSelector(0, 1));
IndexBound hiBound = new IndexBound(row(itemOidIidIndexRowType, 20, 20), new SetColumnSelector(0, 1));
IndexKeyRange keyRange = IndexKeyRange.bounded(itemOidIidIndexRowType, loBound, false, hiBound, false);
Operator indexScan = indexScan_Default(itemOidIidIndexRowType, false, keyRange);
String[] expected = new String[]{};
compareRenderedHKeys(expected, cursor(indexScan, queryContext, queryBindings));
}
//
@Test
public void testExactMatchAbsent()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(299, true, 299, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testExactMatchPresent()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(212, true, 212, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 21, 212)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testEmptyRange()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(218, true, 219, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{};
compareRenderedHKeys(expected, cursor);
}
// The next three tests are light tests of unbounded multi-column index scanning, including mixed-mode.
// More serious tests of index scans with mixed-mode and bounds are in IndexScanBoundedIT
@Test
public void testFullScan()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType);
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{
hkey(1, 11, 111),
hkey(1, 11, 112),
hkey(1, 12, 121),
hkey(1, 12, 122),
hkey(2, 21, 211),
hkey(2, 21, 212),
hkey(2, 22, 221),
hkey(2, 22, 222),
};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testFullScanReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true);
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{
hkey(2, 22, 222),
hkey(2, 22, 221),
hkey(2, 21, 212),
hkey(2, 21, 211),
hkey(1, 12, 122),
hkey(1, 12, 121),
hkey(1, 11, 112),
hkey(1, 11, 111),
};
compareRenderedHKeys(expected, cursor);
}
// Naming scheme for next tests:
// testLoABHiCD
// A: Inclusive/Exclusive for lo bound
// B: Match/Miss indicates whether the lo bound matches or misses an actual value in the db
// C: Inclusive/Exclusive for hi bound
// D: Match/Miss indicates whether the hi bound matches or misses an actual value in the db
@Test
public void testLoInclusiveMatchHiInclusiveMatch()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(121, true, 211, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 121), hkey(1, 12, 122), hkey(2, 21, 211)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMatchHiInclusiveMiss()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(212, true, 223, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 21, 212), hkey(2, 22, 221), hkey(2, 22, 222)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMatchHiExclusiveMatch()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(212, true, 222, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 21, 212), hkey(2, 22, 221)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMatchHiExclusiveMiss()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(212, true, 223, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 21, 212), hkey(2, 22, 221), hkey(2, 22, 222)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMissHiInclusiveMatch()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(100, true, 121, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 11, 111), hkey(1, 11, 112), hkey(1, 12, 121)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMissHiInclusiveMiss()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(100, true, 125, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 11, 111), hkey(1, 11, 112), hkey(1, 12, 121), hkey(1, 12, 122)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMissHiExclusiveMatch()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(100, true, 122, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 11, 111), hkey(1, 11, 112), hkey(1, 12, 121)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMissHiExclusiveMiss()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(100, true, 125, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 11, 111), hkey(1, 11, 112), hkey(1, 12, 121), hkey(1, 12, 122)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMatchHiInclusiveMatch()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(121, false, 211, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 122), hkey(2, 21, 211)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMatchHiInclusiveMiss()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(212, false, 223, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 22, 221), hkey(2, 22, 222)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMatchHiExclusiveMatch()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(212, false, 222, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 22, 221)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMatchHiExclusiveMiss()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(212, false, 223, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 22, 221), hkey(2, 22, 222)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMissHiInclusiveMatch()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(100, false, 121, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 11, 111), hkey(1, 11, 112), hkey(1, 12, 121)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMissHiInclusiveMiss()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(100, false, 125, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 11, 111), hkey(1, 11, 112), hkey(1, 12, 121), hkey(1, 12, 122)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMissHiExclusiveMatch()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(100, false, 122, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 11, 111), hkey(1, 11, 112), hkey(1, 12, 121)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMissHiExclusiveMiss()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, false, iidKeyRange(100, false, 125, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 11, 111), hkey(1, 11, 112), hkey(1, 12, 121), hkey(1, 12, 122)};
compareRenderedHKeys(expected, cursor);
}
// Reverse versions of above tests
@Test
public void testExactMatchAbsentReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(299, true, 299, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testExactMatchPresentReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(212, true, 212, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 21, 212)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testEmptyRangeReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(218, true, 219, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMatchHiInclusiveMatchReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(121, true, 211, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 21, 211), hkey(1, 12, 122), hkey(1, 12, 121)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMatchHiInclusiveMissReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(212, true, 223, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 22, 222), hkey(2, 22, 221), hkey(2, 21, 212)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMatchHiExclusiveMatchReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(212, true, 222, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 22, 221), hkey(2, 21, 212)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMatchHiExclusiveMissReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(212, true, 223, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 22, 222), hkey(2, 22, 221), hkey(2, 21, 212)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMissHiInclusiveMatchReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(100, true, 121, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 121), hkey(1, 11, 112), hkey(1, 11, 111)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMissHiInclusiveMissReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(100, true, 125, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 122), hkey(1, 12, 121), hkey(1, 11, 112), hkey(1, 11, 111)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMissHiExclusiveMatchReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(100, true, 122, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 121), hkey(1, 11, 112), hkey(1, 11, 111)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoInclusiveMissHiExclusiveMissReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(100, true, 125, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 122), hkey(1, 12, 121), hkey(1, 11, 112), hkey(1, 11, 111)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMatchHiInclusiveMatchReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(121, false, 211, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 21, 211), hkey(1, 12, 122)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMatchHiInclusiveMissReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(212, false, 223, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 22, 222), hkey(2, 22, 221)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMatchHiExclusiveMatchReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(212, false, 222, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 22, 221)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMatchHiExclusiveMissReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(212, false, 223, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(2, 22, 222), hkey(2, 22, 221)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMissHiInclusiveMatchReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(100, false, 121, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 121), hkey(1, 11, 112), hkey(1, 11, 111)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMissHiInclusiveMissReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(100, false, 125, true));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 122), hkey(1, 12, 121), hkey(1, 11, 112), hkey(1, 11, 111)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMissHiExclusiveMatchReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(100, false, 122, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 121), hkey(1, 11, 112), hkey(1, 11, 111)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testLoExclusiveMissHiExclusiveMissReverse()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(100, false, 125, false));
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
String[] expected = new String[]{hkey(1, 12, 122), hkey(1, 12, 121), hkey(1, 11, 112), hkey(1, 11, 111)};
compareRenderedHKeys(expected, cursor);
}
@Test
public void testCursor()
{
Operator indexScan = indexScan_Default(itemIidIndexRowType, true, iidKeyRange(100, false, 125, false));
CursorLifecycleTestCase testCase = new CursorLifecycleTestCase()
{
@Override
public boolean hKeyComparison()
{
return true;
}
@Override
public String[] firstExpectedHKeys()
{
return new String[]{hkey(1, 12, 122), hkey(1, 12, 121), hkey(1, 11, 112), hkey(1, 11, 111)};
}
};
testCursorLifecycle(indexScan, testCase);
}
// Inspired by bug 898013
@Test
public void testAliasingOfPersistitIndexRowFields()
{
Operator indexScan = indexScan_Default(itemOidIidIndexRowType, false, null);
Cursor cursor = cursor(indexScan, queryContext, queryBindings);
cursor.openTopLevel();
Row row = cursor.next();
// Get and checking each field should work
assertEquals(11, row.value(0).getInt32());
assertEquals(111, row.value(1).getInt32());
// Getting all value sources and then using them should also work
ValueSource v0 = row.value(0);
ValueSource v1 = row.value(1);
assertEquals(11, v0.getInt32());
assertEquals(111, v1.getInt32());
}
// For use by this class
private IndexKeyRange iidKeyRange(int lo, boolean loInclusive, int hi, boolean hiInclusive)
{
return IndexKeyRange.bounded(itemIidIndexRowType, iidBound(lo), loInclusive, iidBound(hi), hiInclusive);
}
private IndexBound iidBound(int iid)
{
return new IndexBound(row(itemIidIndexRowType, iid), new SetColumnSelector(0));
}
private String hkey(int cid, int oid, int iid)
{
return String.format("{1,(long)%s,2,(long)%s,3,(long)%s}", cid, oid, iid);
}
}