/**
* 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.Operator;
import com.foundationdb.qp.row.Row;
import com.foundationdb.qp.rowtype.IndexRowType;
import com.foundationdb.qp.rowtype.RowType;
import com.foundationdb.qp.rowtype.Schema;
import com.foundationdb.server.api.dml.SetColumnSelector;
import org.junit.Test;
import java.util.EnumSet;
import static com.foundationdb.qp.operator.API.*;
import static com.foundationdb.server.test.ExpressionGenerators.field;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
// From bug #1081396.
public class GroupSkipScanIT extends OperatorITBase
{
@Override
protected void setupCreateSchema()
{
p = createTable("schema", "p",
"pid INT PRIMARY KEY NOT NULL",
"pn INT");
createIndex("schema", "p", "p_n", "pn");
c1 = createTable("schema", "c1",
"cid INT PRIMARY KEY NOT NULL",
"pid INT NOT NULL", "GROUPING FOREIGN KEY(pid) REFERENCES p(pid)",
"cn INT");
createIndex("schema", "c1", "c1_n", "cn");
c2 = createTable("schema", "c2",
"cid INT PRIMARY KEY NOT NULL",
"pid INT NOT NULL", "GROUPING FOREIGN KEY(pid) REFERENCES p(pid)",
"cn INT");
createIndex("schema", "c2", "c2_n", "cn");
}
@Override
protected void setupPostCreateSchema()
{
pRowType = schema.tableRowType(table(p));
pNIndexRowType = indexType(p, "pn");
c1RowType = schema.tableRowType(table(c1));
c1NIndexRowType = indexType(c1, "cn");
c2RowType = schema.tableRowType(table(c2));
c2NIndexRowType = indexType(c2, "cn");
queryContext = queryContext(adapter);
queryBindings = queryContext.createBindings();
db = new Row[] {
row(p, 1L, 1L),
row(c1, 101L, 1L, 100L),
row(c1, 102L, 1L, 200L),
row(c2, 121L, 1L, 120L),
row(c2, 122L, 1L, 220L),
row(p, 2L, 2L),
row(c1, 201L, 2L, 100L),
row(c1, 202L, 2L, 200L),
row(c2, 221L, 2L, 120L),
row(c2, 222L, 2L, 220L),
row(p, 3L, 1L),
row(p, 4L, 2L),
row(p, 5L, 1L),
row(p, 6L, 2L),
row(p, 7L, 1L),
row(p, 8L, 2L),
row(p, 9L, 1L),
row(c1, 901L, 9L, 100L),
row(c1, 902L, 9L, 200L),
row(c2, 921L, 9L, 120L),
row(c2, 922L, 9L, 220L),
row(p, 10L, 2L)
};
use(db);
}
private static final IntersectOption OUTPUT = IntersectOption.OUTPUT_LEFT;
private int p, c1, c2;
private RowType pRowType, c1RowType, c2RowType;
private IndexRowType pNIndexRowType, c1NIndexRowType, c2NIndexRowType;
@Test
public void jumpToEqual()
{
Operator plan = jumpToEqual(false);
Row[] expected = new Row[] {
row(c2NIndexRowType, 120L, 1L, 121L),
row(c2NIndexRowType, 120L, 9L, 921L),
};
compareRows(expected, cursor(plan, queryContext, queryBindings));
plan = jumpToEqual(true);
compareRows(expected, cursor(plan, queryContext, queryBindings));
}
private Operator jumpToEqual(boolean skip)
{
Ordering pNOrdering = API.ordering();
pNOrdering.append(field(pNIndexRowType, 1), true);
Ordering c1NOrdering = API.ordering();
c1NOrdering.append(field(c1NIndexRowType, 1), true);
c1NOrdering.append(field(c1NIndexRowType, 2), true);
Ordering c2NOrdering = API.ordering();
c2NOrdering.append(field(c2NIndexRowType, 1), true);
c2NOrdering.append(field(c1NIndexRowType, 2), true);
IntersectOption scanType = skip ? IntersectOption.SKIP_SCAN : IntersectOption.SEQUENTIAL_SCAN;
return intersect_Ordered(
intersect_Ordered(
union_Ordered(
union_Ordered(
indexScan_Default(
c2NIndexRowType,
nEq(c2NIndexRowType, 120),
c2NOrdering),
indexScan_Default(
c2NIndexRowType,
nEq(c2NIndexRowType, 121),
c2NOrdering),
c2NIndexRowType,
c2NIndexRowType,
2,
2,
ascending(true, true),
true),
indexScan_Default(
c2NIndexRowType,
nEq(c2NIndexRowType, 122),
c2NOrdering),
c2NIndexRowType,
c2NIndexRowType,
2,
2,
ascending(true, true),
true),
union_Ordered(
union_Ordered(
indexScan_Default(
pNIndexRowType,
nEq(pNIndexRowType, 0),
pNOrdering),
indexScan_Default(
pNIndexRowType,
nEq(pNIndexRowType, 1),
pNOrdering),
pNIndexRowType,
pNIndexRowType,
1,
1,
ascending(true),
false),
indexScan_Default(
pNIndexRowType,
nEq(pNIndexRowType, 3),
pNOrdering),
pNIndexRowType,
pNIndexRowType,
1,
1,
ascending(true),
false),
c2NIndexRowType,
pNIndexRowType,
2,
1,
ascending(true),
JoinType.INNER_JOIN,
EnumSet.of(OUTPUT, scanType),
null,
true),
union_Ordered(
union_Ordered(
indexScan_Default(
c1NIndexRowType,
nEq(c1NIndexRowType, 100),
c1NOrdering),
indexScan_Default(
c1NIndexRowType,
nEq(c1NIndexRowType, 101),
c1NOrdering),
c1NIndexRowType,
c1NIndexRowType,
2,
2,
ascending(true, true),
false),
indexScan_Default(
c1NIndexRowType,
nEq(c1NIndexRowType, 102),
c1NOrdering),
c1NIndexRowType,
c1NIndexRowType,
2,
2,
ascending(true, true),
false),
c2NIndexRowType,
c1NIndexRowType,
2,
2,
ascending(true),
JoinType.INNER_JOIN,
EnumSet.of(OUTPUT, scanType),
null,
true);
}
private IndexKeyRange nEq(IndexRowType nIndexRowType, long n)
{
IndexBound bound = new IndexBound(row(nIndexRowType, n), new SetColumnSelector(0));
return IndexKeyRange.bounded(nIndexRowType, bound, true, bound, true);
}
private boolean[] ascending(boolean... ascending)
{
return ascending;
}
}