/** * 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.operator.API; import com.foundationdb.qp.operator.Cursor; import com.foundationdb.qp.operator.ExpressionGenerator; import com.foundationdb.qp.operator.Operator; import com.foundationdb.qp.row.BindableRow; import com.foundationdb.qp.row.Row; import com.foundationdb.qp.rowtype.RowType; import com.foundationdb.server.types.mcompat.mtypes.MNumeric; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.foundationdb.server.test.ExpressionGenerators.*; import static com.foundationdb.qp.operator.API.*; public class Sort_InsertionLimitedIT extends OperatorITBase { @Override protected void setupPostCreateSchema() { super.setupPostCreateSchema(); Row[] dbRows = new Row[]{ row(customer, 1L, "northbridge"), row(customer, 2L, "foundation"), row(customer, 4L, "highland"), row(customer, 5L, "matrix"), row(order, 11L, 1L, "ori"), row(order, 12L, 1L, "david"), row(order, 21L, 2L, "david"), row(order, 22L, 2L, "jack"), row(order, 31L, 3L, "david"), row(order, 51L, 5L, "yuval"), row(item, 111L, 11L), row(item, 112L, 11L), row(item, 121L, 12L), row(item, 122L, 12L), row(item, 211L, 21L), row(item, 212L, 21L), row(item, 221L, 22L), row(item, 222L, 22L), }; use(dbRows); } // Sort / InsertionLimited tests @Test public void testCustomerName_Limit0() { Operator plan = sort_InsertionLimited( filter_Default( groupScan_Default(coi), Collections.singleton(customerRowType)), customerRowType, ordering(field(customerRowType, 1), true), SortOption.PRESERVE_DUPLICATES, 0); Cursor cursor = cursor(plan, queryContext, queryBindings); compareRows(new Row[0], cursor); } @Test public void testCustomerName() { Operator plan = sort_InsertionLimited( filter_Default( groupScan_Default(coi), Collections.singleton(customerRowType)), customerRowType, ordering(field(customerRowType, 1), true), SortOption.PRESERVE_DUPLICATES, 2); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(customerRowType, 2L, "foundation"), row(customerRowType, 4L, "highland") }; compareRows(expected, cursor); } @Test public void testOrderSalesmanCid() { Operator plan = sort_InsertionLimited( filter_Default( groupScan_Default(coi), Collections.singleton(orderRowType)), orderRowType, ordering(field(orderRowType, 2), true, field(orderRowType, 1), false), SortOption.PRESERVE_DUPLICATES, 4); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(orderRowType, 31L, 3L, "david"), row(orderRowType, 21L, 2L, "david"), row(orderRowType, 12L, 1L, "david"), row(orderRowType, 22L, 2L, "jack"), }; compareRows(expected, cursor); } @Test public void testOrderSalesman() { Operator plan = sort_InsertionLimited( filter_Default( groupScan_Default(coi), Collections.singleton(orderRowType)), orderRowType, ordering(field(orderRowType, 2), true), SortOption.PRESERVE_DUPLICATES, 4); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ // Order among equals in group. row(orderRowType, 12L, 1L, "david"), row(orderRowType, 21L, 2L, "david"), row(orderRowType, 31L, 3L, "david"), row(orderRowType, 22L, 2L, "jack"), }; compareRows(expected, cursor); } @Test public void testOrderSalesman2() { Operator plan = sort_InsertionLimited( filter_Default( groupScan_Default(coi), Collections.singleton(orderRowType)), orderRowType, ordering(field(orderRowType, 2), true), SortOption.PRESERVE_DUPLICATES, 2); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ // Kept earlier ones in group (fewer inserts). row(orderRowType, 12L, 1L, "david"), row(orderRowType, 21L, 2L, "david"), }; compareRows(expected, cursor); } @Test public void testAAA() { Operator flattenOI = flatten_HKeyOrdered( groupScan_Default(coi), orderRowType, itemRowType, JoinType.INNER_JOIN); RowType oiType = flattenOI.rowType(); // flattenOI columns: oid, cid, salesman, iid, oid ExpressionGenerator cidField = field(oiType, 1); ExpressionGenerator oidField = field(oiType, 0); ExpressionGenerator iidField = field(oiType, 3); Operator plan = sort_InsertionLimited( filter_Default( flattenOI, Collections.singleton(oiType)), oiType, ordering(cidField, true, oidField, true, iidField, true), SortOption.PRESERVE_DUPLICATES, 8); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(oiType, 11L, 1L, "ori", 111L, 11L), row(oiType, 11L, 1L, "ori", 112L, 11L), row(oiType, 12L, 1L, "david", 121L, 12L), row(oiType, 12L, 1L, "david", 122L, 12L), row(oiType, 21L, 2L, "david", 211L, 21L), row(oiType, 21L, 2L, "david", 212L, 21L), row(oiType, 22L, 2L, "jack", 221L, 22L), row(oiType, 22L, 2L, "jack", 222L, 22L), }; compareRows(expected, cursor); } @Test public void testAAD() { Operator flattenOI = flatten_HKeyOrdered( groupScan_Default(coi), orderRowType, itemRowType, JoinType.INNER_JOIN); RowType oiType = flattenOI.rowType(); // flattenOI columns: oid, cid, salesman, iid, oid ExpressionGenerator cidField = field(oiType, 1); ExpressionGenerator oidField = field(oiType, 0); ExpressionGenerator iidField = field(oiType, 3); Operator plan = sort_InsertionLimited( filter_Default( flattenOI, Collections.singleton(oiType)), oiType, ordering(cidField, true, oidField, true, iidField, false), SortOption.PRESERVE_DUPLICATES, 8); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(oiType, 11L, 1L, "ori", 112L, 11L), row(oiType, 11L, 1L, "ori", 111L, 11L), row(oiType, 12L, 1L, "david", 122L, 12L), row(oiType, 12L, 1L, "david", 121L, 12L), row(oiType, 21L, 2L, "david", 212L, 21L), row(oiType, 21L, 2L, "david", 211L, 21L), row(oiType, 22L, 2L, "jack", 222L, 22L), row(oiType, 22L, 2L, "jack", 221L, 22L), }; compareRows(expected, cursor); } @Test public void testADA() { Operator flattenOI = flatten_HKeyOrdered( groupScan_Default(coi), orderRowType, itemRowType, JoinType.INNER_JOIN); RowType oiType = flattenOI.rowType(); // flattenOI columns: oid, cid, salesman, iid, oid ExpressionGenerator cidField = field(oiType, 1); ExpressionGenerator oidField = field(oiType, 0); ExpressionGenerator iidField = field(oiType, 3); Operator plan = sort_InsertionLimited( filter_Default( flattenOI, Collections.singleton(oiType)), oiType, ordering(cidField, true, oidField, false, iidField, true), SortOption.PRESERVE_DUPLICATES, 8); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(oiType, 12L, 1L, "david", 121L, 12L), row(oiType, 12L, 1L, "david", 122L, 12L), row(oiType, 11L, 1L, "ori", 111L, 11L), row(oiType, 11L, 1L, "ori", 112L, 11L), row(oiType, 22L, 2L, "jack", 221L, 22L), row(oiType, 22L, 2L, "jack", 222L, 22L), row(oiType, 21L, 2L, "david", 211L, 21L), row(oiType, 21L, 2L, "david", 212L, 21L), }; compareRows(expected, cursor); } @Test public void testADD() { Operator flattenOI = flatten_HKeyOrdered( groupScan_Default(coi), orderRowType, itemRowType, JoinType.INNER_JOIN); RowType oiType = flattenOI.rowType(); // flattenOI columns: oid, cid, salesman, iid, oid ExpressionGenerator cidField = field(oiType, 1); ExpressionGenerator oidField = field(oiType, 0); ExpressionGenerator iidField = field(oiType, 3); Operator plan = sort_InsertionLimited( filter_Default( flattenOI, Collections.singleton(oiType)), oiType, ordering(cidField, true, oidField, false, iidField, false), SortOption.PRESERVE_DUPLICATES, 8); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(oiType, 12L, 1L, "david", 122L, 12L), row(oiType, 12L, 1L, "david", 121L, 12L), row(oiType, 11L, 1L, "ori", 112L, 11L), row(oiType, 11L, 1L, "ori", 111L, 11L), row(oiType, 22L, 2L, "jack", 222L, 22L), row(oiType, 22L, 2L, "jack", 221L, 22L), row(oiType, 21L, 2L, "david", 212L, 21L), row(oiType, 21L, 2L, "david", 211L, 21L), }; compareRows(expected, cursor); } @Test public void testDAA() { Operator flattenOI = flatten_HKeyOrdered( groupScan_Default(coi), orderRowType, itemRowType, JoinType.INNER_JOIN); RowType oiType = flattenOI.rowType(); // flattenOI columns: oid, cid, salesman, iid, oid ExpressionGenerator cidField = field(oiType, 1); ExpressionGenerator oidField = field(oiType, 0); ExpressionGenerator iidField = field(oiType, 3); Operator plan = sort_InsertionLimited( filter_Default( flattenOI, Collections.singleton(oiType)), oiType, ordering(cidField, false, oidField, true, iidField, true), SortOption.PRESERVE_DUPLICATES, 8); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(oiType, 21L, 2L, "david", 211L, 21L), row(oiType, 21L, 2L, "david", 212L, 21L), row(oiType, 22L, 2L, "jack", 221L, 22L), row(oiType, 22L, 2L, "jack", 222L, 22L), row(oiType, 11L, 1L, "ori", 111L, 11L), row(oiType, 11L, 1L, "ori", 112L, 11L), row(oiType, 12L, 1L, "david", 121L, 12L), row(oiType, 12L, 1L, "david", 122L, 12L), }; compareRows(expected, cursor); } @Test public void testDAD() { Operator flattenOI = flatten_HKeyOrdered( groupScan_Default(coi), orderRowType, itemRowType, JoinType.INNER_JOIN); RowType oiType = flattenOI.rowType(); // flattenOI columns: oid, cid, salesman, iid, oid ExpressionGenerator cidField = field(oiType, 1); ExpressionGenerator oidField = field(oiType, 0); ExpressionGenerator iidField = field(oiType, 3); Operator plan = sort_InsertionLimited( filter_Default( flattenOI, Collections.singleton(oiType)), oiType, ordering(cidField, false, oidField, true, iidField, false), SortOption.PRESERVE_DUPLICATES, 8); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(oiType, 21L, 2L, "david", 212L, 21L), row(oiType, 21L, 2L, "david", 211L, 21L), row(oiType, 22L, 2L, "jack", 222L, 22L), row(oiType, 22L, 2L, "jack", 221L, 22L), row(oiType, 11L, 1L, "ori", 112L, 11L), row(oiType, 11L, 1L, "ori", 111L, 11L), row(oiType, 12L, 1L, "david", 122L, 12L), row(oiType, 12L, 1L, "david", 121L, 12L), }; compareRows(expected, cursor); } @Test public void testDDA() { Operator flattenOI = flatten_HKeyOrdered( groupScan_Default(coi), orderRowType, itemRowType, JoinType.INNER_JOIN); RowType oiType = flattenOI.rowType(); // flattenOI columns: oid, cid, salesman, iid, oid ExpressionGenerator cidField = field(oiType, 1); ExpressionGenerator oidField = field(oiType, 0); ExpressionGenerator iidField = field(oiType, 3); Operator plan = sort_InsertionLimited( filter_Default( flattenOI, Collections.singleton(oiType)), oiType, ordering(cidField, false, oidField, false, iidField, true), SortOption.PRESERVE_DUPLICATES, 8); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(oiType, 22L, 2L, "jack", 221L, 22L), row(oiType, 22L, 2L, "jack", 222L, 22L), row(oiType, 21L, 2L, "david", 211L, 21L), row(oiType, 21L, 2L, "david", 212L, 21L), row(oiType, 12L, 1L, "david", 121L, 12L), row(oiType, 12L, 1L, "david", 122L, 12L), row(oiType, 11L, 1L, "ori", 111L, 11L), row(oiType, 11L, 1L, "ori", 112L, 11L), }; compareRows(expected, cursor); } @Test public void testDDD() { Operator flattenOI = flatten_HKeyOrdered( groupScan_Default(coi), orderRowType, itemRowType, JoinType.INNER_JOIN); RowType oiType = flattenOI.rowType(); // flattenOI columns: oid, cid, salesman, iid, oid ExpressionGenerator cidField = field(oiType, 1); ExpressionGenerator oidField = field(oiType, 0); ExpressionGenerator iidField = field(oiType, 3); Operator plan = sort_InsertionLimited( filter_Default( flattenOI, Collections.singleton(oiType)), oiType, ordering(cidField, false, oidField, false, iidField, false), SortOption.PRESERVE_DUPLICATES, 8); Cursor cursor = cursor(plan, queryContext, queryBindings); Row[] expected = new Row[]{ row(oiType, 22L, 2L, "jack", 222L, 22L), row(oiType, 22L, 2L, "jack", 221L, 22L), row(oiType, 21L, 2L, "david", 212L, 21L), row(oiType, 21L, 2L, "david", 211L, 21L), row(oiType, 12L, 1L, "david", 122L, 12L), row(oiType, 12L, 1L, "david", 121L, 12L), row(oiType, 11L, 1L, "ori", 112L, 11L), row(oiType, 11L, 1L, "ori", 111L, 11L), }; compareRows(expected, cursor); } @Test public void testPreserveDuplicates() { Operator project = project_DefaultTest( filter_Default( groupScan_Default(coi), Collections.singleton(orderRowType)), orderRowType, Arrays.asList(field(orderRowType, 1))); RowType projectType = project.rowType(); Operator plan = sort_InsertionLimited( project, projectType, ordering(field(projectType, 0), true), SortOption.PRESERVE_DUPLICATES, 5); Row[] expected = new Row[]{ row(projectType, 1L), row(projectType, 1L), row(projectType, 2L), row(projectType, 2L), row(projectType, 3L), }; compareRows(expected, cursor(plan, queryContext, queryBindings)); } @Test public void testSuppressDuplicateCID() { Operator project = project_DefaultTest( filter_Default( groupScan_Default(coi), Collections.singleton(orderRowType)), orderRowType, Arrays.asList(field(orderRowType, 1))); RowType projectType = project.rowType(); Operator plan = sort_InsertionLimited( project, projectType, ordering(field(projectType, 0), true), SortOption.SUPPRESS_DUPLICATES, 4); Row[] expected = new Row[]{ row(projectType, 1L), row(projectType, 2L), row(projectType, 3L), row(projectType, 5L), }; compareRows(expected, cursor(plan, queryContext, queryBindings)); } @Test public void testSuppressDuplicateName() { Operator project = project_DefaultTest( filter_Default( groupScan_Default(coi), Collections.singleton(orderRowType)), orderRowType, Arrays.asList(field(orderRowType, 2))); RowType projectType = project.rowType(); Operator plan = sort_InsertionLimited( project, projectType, ordering(field(projectType, 0), true), SortOption.SUPPRESS_DUPLICATES, 2); Row[] expected = new Row[]{ row(projectType, "david"), row(projectType, "jack"), }; compareRows(expected, cursor(plan, queryContext, queryBindings)); } @Test public void testFreeze() { RowType innerValuesRowType = schema.newValuesType(MNumeric.INT.instance(true)); List<BindableRow> innerValuesRows = new ArrayList<>(); innerValuesRows.add(BindableRow.of(innerValuesRowType, Collections.singletonList(literal(null)))); Operator project = project_DefaultTest(valuesScan_Default(innerValuesRows, innerValuesRowType), innerValuesRowType, Arrays.asList(boundField(customerRowType, 0, 1))); RowType projectType = project.rowType(); Operator plan = sort_InsertionLimited( map_NestedLoops( filter_Default(groupScan_Default(coi), Collections.singleton(customerRowType)), project, 0, pipelineMap(), 1), projectType, ordering(field(projectType, 0), true), SortOption.PRESERVE_DUPLICATES, 4); Row[] expected = new Row[]{ row(projectType, "foundation"), row(projectType, "highland"), row(projectType, "matrix"), row(projectType, "northbridge"), }; compareRows(expected, cursor(plan, queryContext, queryBindings)); } @Test public void testCursor() { Operator plan = sort_InsertionLimited( filter_Default( groupScan_Default(coi), Collections.singleton(orderRowType)), orderRowType, ordering(field(orderRowType, 2), true, field(orderRowType, 1), false), SortOption.PRESERVE_DUPLICATES, 4); CursorLifecycleTestCase testCase = new CursorLifecycleTestCase() { @Override public Row[] firstExpectedRows() { return new Row[] { row(orderRowType, 31L, 3L, "david"), row(orderRowType, 21L, 2L, "david"), row(orderRowType, 12L, 1L, "david"), row(orderRowType, 22L, 2L, "jack"), }; } }; testCursorLifecycle(plan, testCase); } 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; } }