/** * 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.ais.model.Group; 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.Row; import com.foundationdb.qp.rowtype.RowType; import com.foundationdb.qp.rowtype.Schema; import com.foundationdb.server.types.value.ValueSources; import org.junit.Test; import java.util.*; import static com.foundationdb.server.test.ExpressionGenerators.field; import static com.foundationdb.qp.operator.API.*; // More Sort_General testing, with long string values public class Sort_General_LargeKeyIT extends OperatorITBase { @Override protected void setupCreateSchema() { // Don't call super.before(). This is a different schema from most // operator ITs. t = createTable("schema", "t", "a int not null", "b varchar(65535) not null", "c varchar(65535) not null", "d varchar(65535) not null", "id int not null primary key"); } @Override protected void setupPostCreateSchema() { tRowType = schema.tableRowType(table(t)); group = group(t); List<Row> rows = new ArrayList<>(); Random random = new Random(123456789); long key = 0; for (long a = 0; a < A; a++) { int nB = random.nextInt(R) + 1; int lB = random.nextInt(MAX_STRING_LENGTH) + 48; for (long b = 0; b < nB; b++) { int nC = random.nextInt(R) + 1; int lC = random.nextInt(MAX_STRING_LENGTH) + 48; for (long c = 0; c < nC; c++) { int nD = random.nextInt(R) + 1; int lD = random.nextInt(MAX_STRING_LENGTH) + 48; for (long d = 0; d < nD; d++) { Row row = row(t, a, str(b, lB), str(c, lC), str(d, lD), key++); rows.add(row); } } } } db = new Row[rows.size()]; rows.toArray(db); queryContext = queryContext(adapter); queryBindings = queryContext.createBindings(); use(db); } private String str(final long value, final int length) { StringBuilder sb = new StringBuilder(String.format("%048d", value)); while (sb.length() < length) { sb.append("x"); } return sb.toString(); } @Test public void testSort() { for (int x = 0; x < 16; x++) { boolean aAsc = (x & 8) != 0; boolean bAsc = (x & 4) != 0; boolean cAsc = (x & 2) != 0; boolean dAsc = (x & 1) != 0; Operator plan = sort_General( groupScan_Default(group), tRowType, ordering(field(tRowType, 0), aAsc, field(tRowType, 1), bAsc, field(tRowType, 2), cAsc, field(tRowType, 3), dAsc), SortOption.PRESERVE_DUPLICATES); Cursor cursor = cursor(plan, queryContext, queryBindings); compareRows(expected(aAsc, bAsc, cAsc, dAsc), cursor); } } private Row[] expected(final boolean... asc) { Row[] sorted = new Row[db.length]; Comparator<Row> comparator = new Comparator<Row>() { @Override public int compare(Row x, Row y) { int c = 0; for (int i = 0; c == 0 && i < 4; i++) { c = compare(x, y, asc, i); } return c; } private int compare(Row x, Row y, boolean[] asc, int i) { return compareXY(ValueSources.toObject(x.value(i)), ValueSources.toObject(y.value(i))) * (asc[i] ? 1 : -1); } }; Arrays.sort(db, comparator); int r = 0; for (Row dbRow : db) { sorted[r++] = dbRow; } return sorted; } @SuppressWarnings({ "rawtypes", "unchecked" }) static int compareXY(final Object x, final Object y) { return ((Comparable) x).compareTo((Comparable) y); } 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 static final int A = 100; // Number of distinct t.a values private static final int R = 3; // Maximum number of t.b values per a, c // values per b, d values per c private static final int MAX_STRING_LENGTH = 1000; private int t; private RowType tRowType; private Group group; }