/**
* 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.sort;
import com.foundationdb.qp.operator.API;
import com.foundationdb.qp.operator.Cursor;
import com.foundationdb.qp.operator.QueryBindings;
import com.foundationdb.qp.operator.QueryContext;
import com.foundationdb.qp.operator.RowCursor;
import com.foundationdb.qp.operator.RowsBuilder;
import com.foundationdb.qp.operator.StoreAdapter;
import com.foundationdb.qp.operator.TestOperator;
import com.foundationdb.qp.storeadapter.Sorter;
import com.foundationdb.qp.row.Row;
import com.foundationdb.qp.rowtype.RowType;
import com.foundationdb.qp.rowtype.Schema;
import com.foundationdb.qp.util.SchemaCache;
import com.foundationdb.server.test.it.ITBase;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.server.types.mcompat.mtypes.MString;
import com.foundationdb.util.tap.InOutTap;
import com.foundationdb.util.tap.Tap;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.foundationdb.server.test.ExpressionGenerators.field;
import static org.junit.Assert.assertEquals;
public abstract class SorterITBase extends ITBase {
private static final Boolean ASC = Boolean.TRUE;
private static final Boolean DESC = Boolean.FALSE;
private static InOutTap TEST_TAP = Tap.createTimer("test");
private static final List<String[]> SINGLE_UNORDERED = list("beta", "alpha", "gamma", "delta");
private static final List<String[]> SINGLE_ASCENDING = list("alpha", "beta", "delta", "gamma");
private static final List<String[]> SINGLE_DESCENDING = list("gamma", "delta", "beta", "alpha");
private static final List<String[]> MULTI_UNORDERED = list("a,b", "a,a", "b,b", "b,a");
private static final List<String[]> MULTI_ASC_ASC = list("a,a", "a,b", "b,a", "b,b");
private static final List<String[]> MULTI_ASC_DESC = list("a,b", "a,a", "b,b", "b,a");
private static final List<String[]> MULTI_DESC_DESC = list("b,b", "b,a", "a,b", "a,a");
private static final List<String[]> MULTI_DESC_ASC = list("b,a", "b,b", "a,a", "a,b");
public abstract Sorter createSorter(QueryContext context,
QueryBindings bindings,
Cursor input,
RowType rowType,
API.Ordering ordering,
API.SortOption sortOption,
InOutTap loadTap);
@Test
public void singleFieldAscending() {
runTest(API.SortOption.PRESERVE_DUPLICATES, SINGLE_UNORDERED, SINGLE_ASCENDING, ASC);
}
@Test
public void singleFieldDescending() {
runTest(API.SortOption.PRESERVE_DUPLICATES, SINGLE_UNORDERED, SINGLE_DESCENDING, DESC);
}
@Test
public void multiFieldAscAsc() {
runTest(API.SortOption.PRESERVE_DUPLICATES, MULTI_UNORDERED, MULTI_ASC_ASC, ASC, ASC);
}
@Test
public void multiFieldAscDesc() {
runTest(API.SortOption.PRESERVE_DUPLICATES, MULTI_UNORDERED, MULTI_ASC_DESC, ASC, DESC);
}
@Test
public void multiFieldDescDesc() {
runTest(API.SortOption.PRESERVE_DUPLICATES, MULTI_UNORDERED, MULTI_DESC_DESC, DESC, DESC);
}
@Test
public void multiFieldDescAsc() {
runTest(API.SortOption.PRESERVE_DUPLICATES, MULTI_UNORDERED, MULTI_DESC_ASC, DESC, ASC);
}
@Test
public void firstRowNullAscending() {
List<String[]> input = new ArrayList<>();
input.add(new String[]{null});
input.addAll(SINGLE_UNORDERED);
List<String[]> expected = new ArrayList<>();
expected.add(new String[]{null});
expected.addAll(SINGLE_ASCENDING);
runTest(API.SortOption.PRESERVE_DUPLICATES, input, expected, ASC);
}
protected static List<String[]> list(String... values) {
List<String[]> rows = new ArrayList<>();
for(String s : values) {
String[] fields = s.split(",");
rows.add(fields);
}
return rows;
}
protected static RowsBuilder createBuilder(List<String[]> values) {
TInstance[] tinsts = new TInstance[values.get(0).length];
Arrays.fill(tinsts, MString.varchar());
RowsBuilder rowsBuilder = new RowsBuilder(tinsts);
for(String[] s : values) {
rowsBuilder.row(s);
}
return rowsBuilder;
}
protected void runTest(API.SortOption sortOption, List<String[]> input, List<String[]> expected, boolean... fieldOrdering) {
assertEquals("input = expected size", input.size(), expected.size());
RowsBuilder inputRows = createBuilder(input);
Schema schema = SchemaCache.globalSchema(ddl().getAIS(session()));
StoreAdapter adapter = newStoreAdapter(schema);
TestOperator inputOperator = new TestOperator(inputRows);
QueryContext context = queryContext(adapter);
QueryBindings bindings = context.createBindings();
Cursor inputCursor = API.cursor(inputOperator, context, bindings);
inputCursor.openTopLevel();
API.Ordering ordering = API.ordering();
for(int i = 0; i < fieldOrdering.length; ++i) {
ordering.append(field(inputOperator.rowType(), i), fieldOrdering[i]);
}
Sorter sorter = createSorter(context, bindings, inputCursor, inputOperator.rowType(), ordering, sortOption, TEST_TAP);
RowCursor sortedCursor = sorter.sort();
Row[] expectedRows = createBuilder(expected).rows().toArray(new Row[expected.size()]);
compareRows(expectedRows, sortedCursor);
}
}