/** * 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.IndexKeyRange; import com.foundationdb.qp.operator.API; import com.foundationdb.qp.operator.ExpressionGenerator; 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.error.SetWrongNumColumns; import org.junit.Test; import static com.foundationdb.qp.operator.API.*; import static com.foundationdb.server.test.ExpressionGenerators.field; import static org.junit.Assert.fail; public class Intersect_OrderedIT extends OperatorITBase { @Override protected void setupCreateSchema() { t = createTable( "schema", "t", "pid int not null primary key", "x int"); u = createTable( "schema", "u", "pid int not null primary key", "x int"); v = createTable( "schema", "v", "pid int not null primary key", "x int"); w = createTable( "schema", "w", "pid int not null primary key", "x int"); createIndex("schema", "t", "idx_x", "x"); createIndex("schema", "u", "idx_x", "x"); createIndex("schema", "v", "idx_x", "x"); createIndex("schema", "w", "idx_x", "x"); } @Override protected void setupPostCreateSchema() { uRowType = schema.tableRowType(table(u)); tPidIndexRowType = indexType(t, "pid"); tXIndexRowType = indexType(t, "x"); uXIndexRowType = indexType(u, "x"); vXIndexRowType = indexType(v, "x"); wXIndexRowType = indexType(w, "x"); queryContext = queryContext(adapter); queryBindings = queryContext.createBindings(); db = new Row[] { row(t, 1000L, 10L), row(t, 1001L, 20L), row(t, 1002L, 50L), row(t, 1003L, 80L), row(t, 1004L, 90L), row(t, 1005L, 90L), row(u, 1000L, 1L), row(u, 1001L, 2L), row(u, 1002L, 5L), row(u, 1003L, 8L), row(u, 1004L, 9L), row(u, 1005L, 9L), row(v, 1000L, 0L), row(v, 1001L, 1L), row(v, 1002L, 1L), row(v, 1003L, 2L), row(v, 1004L, 9L), row(v, 1005L, 20L), }; use(db); } private int t,u,v, w; private RowType uRowType; private IndexRowType tPidIndexRowType; private IndexRowType tXIndexRowType,uXIndexRowType, vXIndexRowType, wXIndexRowType; // IllegalArgumentIntersection tests @Test public void testInputNull() { // First input null try { intersect_Ordered(null, groupScan_Default(coi), tXIndexRowType, tXIndexRowType, 1, 1, ascending(true)); fail(); } catch (IllegalArgumentException e) { } // Second input null try { intersect_Ordered(groupScan_Default(coi), null, tXIndexRowType, tXIndexRowType, 1, 1, ascending(true)); fail(); } catch (IllegalArgumentException e) { } } @Test public void testInputType() { // First input type null try { intersect_Ordered(groupScan_Default(coi), groupScan_Default(coi), null, tXIndexRowType, 1, 1, ascending(true)); fail(); } catch (IllegalArgumentException e) { } // Second input type null try { intersect_Ordered(groupScan_Default(coi), groupScan_Default(coi), tXIndexRowType, null, 1, 1, ascending(true)); fail(); } catch (IllegalArgumentException e) { } } @Test (expected = SetWrongNumColumns.class) public void testDifferentInputTypes() { // Test different input types try { intersect_Ordered(groupScan_Default(coi), groupScan_Default(coi), tXIndexRowType, tPidIndexRowType, 1, 1, ascending(true)); fail(); } catch (IllegalArgumentException e) { } } @Test public void testOrderingColumns() { // First ordering fields negative try { intersect_Ordered(groupScan_Default(coi), groupScan_Default(coi), tXIndexRowType, tXIndexRowType, -1, 1, ascending(true)); fail(); } catch (IllegalArgumentException e) { } // Second ordering fields negative try { intersect_Ordered(groupScan_Default(coi), groupScan_Default(coi), tXIndexRowType, tXIndexRowType, 1, -1, ascending(true)); fail(); } catch (IllegalArgumentException e) { } // First ordering fields too high try { intersect_Ordered(groupScan_Default(coi), groupScan_Default(coi), tXIndexRowType, tXIndexRowType, 3, 1, ascending(true)); fail(); } catch (IllegalArgumentException e) { } // Second ordering fields too high try { intersect_Ordered(groupScan_Default(coi), groupScan_Default(coi), tXIndexRowType, tXIndexRowType, 1, 3, ascending(true)); fail(); } catch (IllegalArgumentException e) { } // Different number of ordering fields try { intersect_Ordered(groupScan_Default(coi), groupScan_Default(coi), tXIndexRowType, tXIndexRowType, 1, 2, ascending(true)); fail(); } catch (IllegalArgumentException e) { } } // Runtime tests @Test public void testBothInputsEmpty() { Operator plan = intersectPlan(wXIndexRowType, wXIndexRowType, true); Row[] expected = new Row[] { }; compareRows(expected, cursor(plan, queryContext, queryBindings)); plan = intersectPlan(wXIndexRowType, wXIndexRowType, false); expected = new Row[] { }; compareRows(expected, cursor(plan, queryContext, queryBindings)); ; } @Test public void testLeftEmpty() { Operator plan = intersectPlan( wXIndexRowType,tXIndexRowType, true); Row[] expected = new Row[] { }; compareRows(expected, cursor(plan, queryContext, queryBindings)); plan = intersectPlan( wXIndexRowType, vXIndexRowType, false); expected = new Row[] { }; compareRows(expected, cursor(plan, queryContext, queryBindings)); } @Test public void testRightEmpty() { Operator plan = intersectPlan( uXIndexRowType,wXIndexRowType, true); Row[] expected = new Row[] { }; compareRows(expected, cursor(plan, queryContext, queryBindings)); plan = intersectPlan( uXIndexRowType,wXIndexRowType, false); expected = new Row[] { }; compareRows(expected, cursor(plan, queryContext, queryBindings)); ; } @Test public void testDuplicates() { Operator plan = intersectPlan( uXIndexRowType,uXIndexRowType, true); Row[] expected = new Row[] { row(uRowType, 1L, 1000L), row(uRowType, 2L, 1001L), row(uRowType, 5L, 1002L), row(uRowType, 8L, 1003L), row(uRowType, 9L, 1004L), row(uRowType, 9L, 1005L), }; compareRows(expected, cursor(plan, queryContext, queryBindings)); plan = intersectPlan( uXIndexRowType,uXIndexRowType, false); expected = new Row[] { row(uRowType, 9L, 1005L), row(uRowType, 9L, 1004L), row(uRowType, 8L, 1003L), row(uRowType, 5L, 1002L), row(uRowType, 2L, 1001L), row(uRowType, 1L, 1000L), }; compareRows(expected, cursor(plan, queryContext, queryBindings)); } @Test public void testDisjoint() { Operator plan = intersectPlan( uXIndexRowType,tXIndexRowType, true); Row[] expected = new Row[] { }; compareRows(expected, cursor(plan, queryContext, queryBindings)); plan = intersectPlan( uXIndexRowType,tXIndexRowType, false); expected = new Row[] { }; compareRows(expected, cursor(plan, queryContext, queryBindings)); } @Test public void multiCases() { Operator plan = intersectPlan(uXIndexRowType, vXIndexRowType, false); Row[] expected = new Row[] { row(uXIndexRowType, 9L, 1005L), row(uXIndexRowType, 2L, 1001L), row(uXIndexRowType, 1L, 1000L) }; compareRows(expected, cursor(plan, queryContext, queryBindings)); plan = intersectPlan(uXIndexRowType, vXIndexRowType, false); expected = new Row[] { row(uXIndexRowType, 9L, 1005L), row(uXIndexRowType, 2L, 1001L), row(uXIndexRowType, 1L, 1000L) }; compareRows(expected, cursor(plan, queryContext, queryBindings)); } private Operator intersectPlan(IndexRowType t1, IndexRowType t2, boolean ascending) { Operator plan = intersect_Ordered( indexScan_Default( t1, IndexKeyRange.unbounded(t1), ordering(field(t1, 0), ascending)), indexScan_Default( t2, IndexKeyRange.unbounded(t2), ordering(field(t2, 0), ascending)), t1, t2, 2, 2, ascending(ascending)); return plan; } private Ordering ordering(Object... objects) { Ordering ordering = API.ordering(); int i = 0; while (i < objects.length) { ExpressionGenerator expression = (ExpressionGenerator) objects[i++]; Boolean ascending = (Boolean) objects[i++]; ordering.append(expression, ascending); } return ordering; } private boolean[] ascending(boolean... ascending) { return ascending; } }