blob: 655dbb13dc49249e44318c92bbf5337e0b8e5503 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.phoenix.end2end;
import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Properties;
import org.apache.commons.lang.ArrayUtils;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PDecimal;
import org.apache.phoenix.schema.types.PDouble;
import org.apache.phoenix.schema.types.PFloat;
import org.apache.phoenix.util.PropertiesUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.Lists;
/**
* @since 1.2
*/
public class SortOrderIT extends ParallelStatsDisabledIT {
private String baseTableName;
@Before
public void generateTableName() {
baseTableName = generateUniqueName();
}
@Test
public void noOrder() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (pk VARCHAR NOT NULL PRIMARY KEY)";
runQueryTest(ddl, "pk", new Object[][]{{"a"}, {"b"}, {"c"}}, new Object[][]{{"a"}, {"b"}, {"c"}},
table);
}
@Test
public void noOrderCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid, code))";
Object[][] rows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
runQueryTest(ddl, upsert("oid", "code"), rows, rows, table);
}
@Test
public void ascOrderInlinePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (pk VARCHAR NOT NULL PRIMARY KEY ASC)";
runQueryTest(ddl, "pk", new Object[][]{{"a"}, {"b"}, {"c"}}, new Object[][]{{"a"}, {"b"}, {"c"}},
table);
}
@Test
public void ascOrderCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid ASC, code DESC))";
Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
Object[][] expectedRows = new Object[][]{{"o1", 3}, {"o1", 2}, {"o1", 1}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, table);
}
@Test
public void descOrderInlinePK() throws Exception {
String table = generateUniqueName();
for (String type : new String[]{"CHAR(2)", "VARCHAR"}) {
String ddl = "CREATE table " + table + " (pk ${type} NOT NULL PRIMARY KEY DESC)".replace("${type}", type);
runQueryTest(ddl, "pk", new Object[][]{{"aa"}, {"bb"}, {"cc"}}, new Object[][]{{"cc"}, {"bb"}, {"aa"}},
table);
}
}
@Test
public void descOrderCompositePK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code))";
Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
Object[][] expectedRows = new Object[][]{{"o3", 3}, {"o2", 2}, {"o1", 1}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, table);
}
@Test
public void descOrderCompositePK2() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
Object[][] expectedRows = new Object[][]{{"o1", 3}, {"o1", 2}, {"o1", 1}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, table);
}
@Test
public void equalityDescInlinePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (pk VARCHAR NOT NULL PRIMARY KEY DESC)";
runQueryTest(ddl, upsert("pk"), new Object[][]{{"a"}, {"b"}, {"c"}}, new Object[][]{{"b"}}, new WhereCondition("pk", "=", "'b'"),
table);
}
@Test
public void equalityDescCompositePK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o2", 2}}, new WhereCondition("oid", "=", "'o2'"),
table);
}
@Test
public void equalityDescCompositePK2() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o1", 2}}, new WhereCondition("code", "=", "2"),
table);
}
@Test
public void inDescCompositePK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o1", 2}}, new WhereCondition("code", "IN", "(2)"),
table);
}
@Test
public void inDescCompositePK2() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"o2", 2}}, new WhereCondition("oid", "IN", "('o2')"),
table);
}
@Test
public void likeDescCompositePK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"a1", 1}, {"b2", 2}, {"c3", 3}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"b2", 2}}, new WhereCondition("oid", "LIKE", "('b%')"),
table);
}
@Test
public void likeDescCompositePK2() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code CHAR(2) NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"a1", "11"}, {"b2", "22"}, {"c3", "33"}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, new Object[][]{{"b2", "22"}}, new WhereCondition("code", "LIKE", "('2%')"),
table);
}
@Test
public void greaterThanDescCompositePK3() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o1", 2}, {"o1", 3}};
Object[][] expectedRows = new Object[][]{{"o1", 2}, {"o1", 1}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("code", "<", "3"),
table);
}
@Test
public void substrDescCompositePK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(3) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code ASC))";
Object[][] insertedRows = new Object[][]{{"ao1", 1}, {"bo2", 2}, {"co3", 3}};
Object[][] expectedRows = new Object[][]{{"co3", 3}, {"bo2", 2}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("SUBSTR(oid, 3, 1)", ">", "'1'"),
table);
}
@Test
public void substrDescCompositePK2() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(4) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code ASC))";
Object[][] insertedRows = new Object[][]{{"aaaa", 1}, {"bbbb", 2}, {"cccd", 3}};
Object[][] expectedRows = new Object[][]{{"cccd", 3}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("SUBSTR(oid, 4, 1)", "=", "'d'"),
table);
}
@Test
public void substrFixedLengthDescPK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(3) PRIMARY KEY DESC)";
Object[][] insertedRows = new Object[][]{{"a"}, {"ab"}};
Object[][] expectedRows = new Object[][]{{"ab"}, {"a"} };
runQueryTest(ddl, upsert("oid"), insertedRows, expectedRows, new WhereCondition("SUBSTR(oid, 1, 1)", "=", "'a'"),
table);
}
@Test
public void substrVarLengthDescPK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid VARCHAR PRIMARY KEY DESC)";
Object[][] insertedRows = new Object[][]{{"a"}, {"ab"}};
Object[][] expectedRows = new Object[][]{{"ab"}, {"a"} };
runQueryTest(ddl, upsert("oid"), insertedRows, expectedRows, new WhereCondition("SUBSTR(oid, 1, 1)", "=", "'a'"),
table);
}
@Test
public void likeVarLengthDescPK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid VARCHAR PRIMARY KEY DESC)";
Object[][] insertedRows = new Object[][]{{"a"}, {"ab"}};
Object[][] expectedRows = new Object[][]{{"ab"}, {"a"} };
runQueryTest(ddl, upsert("oid"), insertedRows, expectedRows, new WhereCondition("oid", "like", "'a%'"),
table);
}
@Test
public void likeFixedLengthDescPK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(3) PRIMARY KEY DESC)";
Object[][] insertedRows = new Object[][]{{"a"}, {"ab"}};
Object[][] expectedRows = new Object[][]{{"ab"}, {"a"} };
runQueryTest(ddl, upsert("oid"), insertedRows, expectedRows, new WhereCondition("oid", "like", "'a%'"),
table);
}
@Test
public void decimalRangeDescPK1() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid DECIMAL PRIMARY KEY DESC)";
Connection conn = DriverManager.getConnection(getUrl());
conn.createStatement().execute(ddl);
conn.createStatement().execute("UPSERT INTO " + table + " VALUES(4.99)");
conn.createStatement().execute("UPSERT INTO " + table + " VALUES(4.0)");
conn.createStatement().execute("UPSERT INTO " + table + " VALUES(5.0)");
conn.createStatement().execute("UPSERT INTO " + table + " VALUES(5.001)");
conn.createStatement().execute("UPSERT INTO " + table + " VALUES(5.999)");
conn.createStatement().execute("UPSERT INTO " + table + " VALUES(6.0)");
conn.createStatement().execute("UPSERT INTO " + table + " VALUES(6.001)");
conn.commit();
String query = "SELECT * FROM " + table + " WHERE oid >= 5.0 AND oid < 6.0";
ResultSet rs = conn.createStatement().executeQuery(query);
assertTrue(rs.next());
assertTrue(new BigDecimal("5.999").compareTo(rs.getBigDecimal(1)) == 0);
assertTrue(rs.next());
assertTrue(new BigDecimal("5.001").compareTo(rs.getBigDecimal(1)) == 0);
assertTrue(rs.next());
assertTrue(new BigDecimal("5.0").compareTo(rs.getBigDecimal(1)) == 0);
assertFalse(rs.next());
}
@Test
public void lTrimDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid VARCHAR NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{" o1 ", 1}, {" o2", 2}, {" o3", 3}};
Object[][] expectedRows = new Object[][]{{" o2", 2}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("LTRIM(oid)", "=", "'o2'"),
table);
}
@Test
public void lPadDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid VARCHAR NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"aaaa", 1}, {"bbbb", 2}, {"cccc", 3}};
Object[][] expectedRows = new Object[][]{{"bbbb", 2}};
runQueryTest(ddl, upsert("oid", "code"), insertedRows, expectedRows, new WhereCondition("LPAD(oid, 8, '123')", "=", "'1231bbbb'"),
table);
}
@Test
public void countDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (oid CHAR(2) NOT NULL, code INTEGER NOT NULL constraint pk primary key (oid DESC, code ASC))";
Object[][] insertedRows = new Object[][]{{"o1", 1}, {"o2", 2}, {"o3", 3}};
Object[][] expectedRows = new Object[][]{{3l}};
runQueryTest(ddl, upsert("oid", "code"), select("COUNT(oid)"), insertedRows, expectedRows,
table);
}
@Test
public void sumDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " +
"constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
Object[][] expectedRows = new Object[][]{{60l, bdec(60.6), 96l}};
runQueryTest(ddl, upsert("n1", "n2", "n3"), select("SUM(n1), SUM(n2), SUM(n3)"), insertedRows, expectedRows,
table);
}
@Test
public void avgDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " +
"constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
Object[][] expectedRows = new Object[][]{{new BigDecimal(bint(2), -1), bdec(20.2), BigDecimal.valueOf(32)}};
runQueryTest(ddl, upsert("n1", "n2", "n3"), select("AVG(n1), AVG(n2), AVG(n3)"), insertedRows, expectedRows,
table);
}
@Test
public void minDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " +
"constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
Object[][] expectedRows = new Object[][]{{10, bdec(10.2), 21l}};
runQueryTest(ddl, upsert("n1", "n2", "n3"), select("MIN(n1), MIN(n2), MIN(n3)"), insertedRows, expectedRows,
table);
}
@Test
public void maxDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL " +
"constraint pk primary key (n1 DESC, n2 DESC, n3 DESC))";
Object[][] insertedRows = new Object[][]{{10, bdec(10.2), 21l}, {20, bdec(20.2), 32l}, {30, bdec(30.2), 43l}};
Object[][] expectedRows = new Object[][]{{30, bdec(30.2), 43l}};
runQueryTest(ddl, upsert("n1", "n2", "n3"), select("MAX(n1), MAX(n2), MAX(n3)"), insertedRows, expectedRows,
table);
}
@Test
public void havingSumDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (name CHAR(1) NOT NULL, code INTEGER NOT NULL " +
"constraint pk primary key (name DESC, code DESC))";
Object[][] insertedRows = new Object[][]{{"a", 10}, {"a", 20}, {"b", 100}};
Object[][] expectedRows = new Object[][]{{"a", 30l}};
runQueryTest(ddl, upsert("name", "code"), select("name", "SUM(code)"), insertedRows, expectedRows,
new HavingCondition("name", "SUM(code) = 30"), table);
}
@Test
public void queryDescDateWithExplicitOrderBy() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (c1 CHAR(1) NOT NULL, c2 CHAR(1) NOT NULL, d1 \"DATE\" NOT NULL, c3 CHAR(1) NOT NULL " +
"constraint pk primary key (c1, c2, d1 DESC, c3))";
Object[] row1 = {"1", "2", date(10, 11, 2001), "3"};
Object[] row2 = {"1", "2", date(10, 11, 2003), "3"};
Object[][] insertedRows = new Object[][]{row1, row2};
runQueryTest(ddl, upsert("c1", "c2", "d1", "c3"), select("c1, c2, d1", "c3"), insertedRows, new Object[][]{row2, row1},
null, null, new OrderBy("d1", OrderBy.Direction.DESC), table);
}
@Test
public void additionOnDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL, d1 DATE NOT NULL " +
"constraint pk primary key (n1 DESC, n2 DESC, n3 DESC, d1 DESC))";
Object[][] insertedRows = new Object[][]{
{10, bdec(10.2), 21l, date(1, 10, 2001)}, {20, bdec(20.2), 32l, date(2, 6, 2001)}, {30, bdec(30.2), 43l, date(3, 1, 2001)}};
Object[][] expectedRows = new Object[][]{
{31l, bdec(32.2), 46l, date(3, 5, 2001)}, {21l, bdec(22.2), 35l, date(2, 10, 2001)}, {11l, bdec(12.2), 24l, date(1, 14, 2001)}};
runQueryTest(ddl, upsert("n1", "n2", "n3", "d1"), select("n1+1, n2+2, n3+3", "d1+4"), insertedRows, expectedRows,
table);
}
@Test
public void subtractionOnDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (n1 INTEGER NOT NULL, n2 DECIMAL(10, 2) NOT NULL, n3 BIGINT NOT NULL, d1 DATE NOT NULL " +
"constraint pk primary key (n1 DESC, n2 DESC, n3 DESC, d1 DESC))";
Object[][] insertedRows = new Object[][]{
{10, bdec(10.2), 21l, date(1, 10, 2001)}, {20, bdec(20.2), 32l, date(2, 6, 2001)}, {30, bdec(30.2), 43l, date(3, 10, 2001)}};
Object[][] expectedRows = new Object[][]{
{29l, bdec(28.2), 40l, date(3, 6, 2001)}, {19l, bdec(18.2), 29l, date(2, 2, 2001)}, {9l, bdec(8.2), 18l, date(1, 6, 2001)}};
runQueryTest(ddl, upsert("n1", "n2", "n3", "d1"), select("n1-1, n2-2, n3-3", "d1-4"), insertedRows, expectedRows,
table);
}
@Test
public void lessThanLeadingDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (id INTEGER NOT NULL, \"DATE\" DATE NOT NULL constraint pk primary key (id DESC, \"DATE\"))";
Object[][] insertedRows = new Object[][]{{1, date(1, 1, 2012)}, {3, date(1, 1, 2013)}, {2, date(1, 1, 2011)}};
Object[][] expectedRows = new Object[][]{{1, date(1, 1, 2012)}};
runQueryTest(ddl, upsert("id", "date"), insertedRows, expectedRows, new WhereCondition("id", "<", "2"),
table);
}
@Test
public void lessThanTrailingDescCompositePK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (id INTEGER NOT NULL, \"DATE\" DATE NOT NULL constraint pk primary key (id DESC, \"DATE\"))";
Object[][] insertedRows = new Object[][]{{1, date(1, 1, 2002)}, {3, date(1, 1, 2003)}, {2, date(1, 1, 2001)}};
Object[][] expectedRows = new Object[][]{{2, date(1, 1, 2001)}};
runQueryTest(ddl, upsert("id", "\"DATE\""), insertedRows, expectedRows, new WhereCondition("\"DATE\"", "<", "TO_DATE('02-02-2001','mm-dd-yyyy')"),
table);
}
@Test
public void descVarLengthPK() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (id VARCHAR PRIMARY KEY DESC)";
Object[][] insertedRows = new Object[][]{{"a"}, {"ab"}, {"abc"}};
Object[][] expectedRows = new Object[][]{{"abc"}, {"ab"}, {"a"}};
runQueryTest(ddl, upsert("id"), select("id"), insertedRows, expectedRows,
null, null, new OrderBy("id", OrderBy.Direction.DESC), table);
}
@Test
public void descVarLengthAscPKGT() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (k1 INTEGER NOT NULL, k2 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1, k2))";
Object[][] insertedRows = new Object[][]{{0, null}, {1, "a"}, {2, "b"}, {3, "ba"}, {4, "baa"}, {5, "c"}, {6, "d"}};
Object[][] expectedRows = new Object[][]{{3}, {4}, {5}, {6}};
runQueryTest(ddl, upsert("k1", "k2"), select("k1"), insertedRows, expectedRows,
new WhereCondition("k2", ">", "'b'"), null, null, table);
}
@Test
public void descVarLengthDescPKGT() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (k1 INTEGER NOT NULL, k2 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1, k2 desc))";
Object[][] insertedRows = new Object[][]{{0, null}, {1, "a"}, {2, "b"}, {3, "ba"}, {4, "baa"}, {5, "c"}, {6, "d"}};
Object[][] expectedRows = new Object[][]{{3}, {4}, {5}, {6}};
runQueryTest(ddl, upsert("k1", "k2"), select("k1"), insertedRows, expectedRows,
new WhereCondition("k2", ">", "'b'"), null, null, table);
}
@Test
public void descVarLengthDescPKLTE() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (k1 INTEGER NOT NULL, k2 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1, k2 desc))";
Object[][] insertedRows = new Object[][]{{0, null}, {1, "a"}, {2, "b"}, {3, "ba"}, {4, "bb"}, {5, "bc"}, {6, "bba"}, {7, "c"}};
Object[][] expectedRows = new Object[][]{{1}, {2}, {3}, {4}};
runQueryTest(ddl, upsert("k1", "k2"), select("k1"), insertedRows, expectedRows,
new WhereCondition("k2", "<=", "'bb'"), null, null, table);
}
@Test
public void descVarLengthAscPKLTE() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (k1 INTEGER NOT NULL, k2 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1, k2))";
Object[][] insertedRows = new Object[][]{{0, null}, {1, "a"}, {2, "b"}, {3, "ba"}, {4, "bb"}, {5, "bc"}, {6, "bba"}, {7, "c"}};
Object[][] expectedRows = new Object[][]{{1}, {2}, {3}, {4}};
runQueryTest(ddl, upsert("k1", "k2"), select("k1"), insertedRows, expectedRows,
new WhereCondition("k2", "<=", "'bb'"), null, null, table);
}
@Test
public void varLengthAscLT() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (k1 VARCHAR NOT NULL, k2 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1, k2))";
Object[][] insertedRows = new Object[][]{{"a", ""}, {"b",""}, {"b","a"}};
Object[][] expectedRows = new Object[][]{{"a"}};
runQueryTest(ddl, upsert("k1", "k2"), select("k1"), insertedRows, expectedRows,
new WhereCondition("k1", "<", "'b'"), null, null, table);
}
@Test
public void varLengthDescLT() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (k1 VARCHAR NOT NULL, k2 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1 desc, k2))";
Object[][] insertedRows = new Object[][]{{"a", ""}, {"b",""}, {"b","a"}};
Object[][] expectedRows = new Object[][]{{"a"}};
runQueryTest(ddl, upsert("k1", "k2"), select("k1"), insertedRows, expectedRows,
new WhereCondition("k1", "<", "'b'"), null, null, table);
}
@Test
public void varLengthDescGT() throws Exception {
String table = generateUniqueName();
String ddl = "CREATE table " + table + " (k1 VARCHAR NOT NULL, k2 VARCHAR, CONSTRAINT pk PRIMARY KEY (k1 desc, k2))";
Object[][] insertedRows = new Object[][]{{"a", ""}, {"b",""}, {"b","a"}, {"ba","a"}};
Object[][] expectedRows = new Object[][]{{"ba"}};
runQueryTest(ddl, upsert("k1", "k2"), select("k1"), insertedRows, expectedRows,
new WhereCondition("k1", ">", "'b'"), null, null, table);
}
@Test
public void testNonPKCompare() throws Exception {
List<Integer> expectedResults = Lists.newArrayList(2,3,4);
Integer[] saltBuckets = new Integer[] {null,3};
PDataType[] dataTypes = new PDataType[] {PDecimal.INSTANCE, PDouble.INSTANCE, PFloat.INSTANCE};
for (Integer saltBucket : saltBuckets) {
for (PDataType dataType : dataTypes) {
for (SortOrder sortOrder : SortOrder.values()) {
testCompareCompositeKey(saltBucket, dataType, sortOrder, "", expectedResults, "");
}
}
}
}
@Test
public void testSkipScanCompare() throws Exception {
List<Integer> expectedResults = Lists.newArrayList(2,4);
List<Integer> rExpectedResults = new ArrayList<>(expectedResults);
Collections.reverse(rExpectedResults);
Integer[] saltBuckets = new Integer[] {null,3};
PDataType[] dataTypes = new PDataType[] {PDecimal.INSTANCE, PDouble.INSTANCE, PFloat.INSTANCE};
for (Integer saltBucket : saltBuckets) {
for (PDataType dataType : dataTypes) {
for (SortOrder sortOrder : SortOrder.values()) {
testCompareCompositeKey(saltBucket, dataType, sortOrder, "k1 in (2,4)", expectedResults, "");
testCompareCompositeKey(saltBucket, dataType, sortOrder, "k1 in (2,4)", rExpectedResults, "ORDER BY k1 DESC");
}
}
}
}
private void testCompareCompositeKey(Integer saltBuckets, PDataType dataType, SortOrder sortOrder, String whereClause, List<Integer> expectedResults, String orderBy) throws SQLException {
String tableName = "t_" + saltBuckets + "_" + dataType + "_" + sortOrder + "_" + baseTableName;
String ddl = "create table if not exists " + tableName + " (k1 bigint not null, k2 " + dataType.getSqlTypeName() + (dataType.isFixedWidth() ? " not null" : "") + ", constraint pk primary key (k1,k2 " + sortOrder + "))" + (saltBuckets == null ? "" : (" SALT_BUCKETS= " + saltBuckets));
Connection conn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES));
conn.createStatement().execute(ddl);
if (!dataType.isFixedWidth()) {
conn.createStatement().execute("upsert into " + tableName + " values (0, null)");
}
conn.createStatement().execute("upsert into " + tableName + " values (1, 0.99)");
conn.createStatement().execute("upsert into " + tableName + " values (2, 1.01)");
conn.createStatement().execute("upsert into " + tableName + " values (3, 2.0)");
conn.createStatement().execute("upsert into " + tableName + " values (4, 1.001)");
conn.commit();
String query = "select k1 from " + tableName + " where " + (whereClause.length() > 0 ? (whereClause + " AND ") : "") + " k2>1.0 " + (orderBy.length() == 0 ? "" : orderBy);
try {
ResultSet rs = conn.createStatement().executeQuery(query);
for (int k : expectedResults) {
assertTrue (tableName, rs.next());
assertEquals(tableName, k,rs.getInt(1));
}
assertFalse(tableName, rs.next());
} finally {
conn.close();
}
}
private void runQueryTest(String ddl, String columnName, Object[][] rows,
Object[][] expectedRows, String table) throws Exception {
runQueryTest(ddl, new String[]{columnName}, rows, expectedRows, null, table);
}
private void runQueryTest(String ddl, String[] columnNames, Object[][] rows,
Object[][] expectedRows, String table) throws Exception {
runQueryTest(ddl, columnNames, rows, expectedRows, null, table);
}
private void runQueryTest(String ddl, String[] columnNames, Object[][] rows, Object[][] expectedRows, WhereCondition condition,
String table) throws Exception {
runQueryTest(ddl, columnNames, columnNames, rows, expectedRows, condition, null, null,
table);
}
private void runQueryTest(String ddl, String[] columnNames, String[] projections, Object[][] rows, Object[][] expectedRows,
String table) throws Exception {
runQueryTest(ddl, columnNames, projections, rows, expectedRows, null, null, null, table);
}
private void runQueryTest(String ddl, String[] columnNames, String[] projections, Object[][] rows, Object[][] expectedRows, HavingCondition havingCondition,
String table) throws Exception {
runQueryTest(ddl, columnNames, projections, rows, expectedRows, null, havingCondition, null,
table);
}
private void runQueryTest(String ddl, String[] columnNames, String[] projections, Object[][] rows, Object[][] expectedRows,
WhereCondition whereCondition, HavingCondition havingCondition, OrderBy orderBy,
String table)
throws Exception
{
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
Connection conn = DriverManager.getConnection(getUrl(), props);
try {
conn.setAutoCommit(false);
createTestTable(getUrl(), ddl);
String columns = appendColumns(columnNames);
String placeholders = appendPlaceholders(columnNames);
String dml = "UPSERT INTO " + table + " (" + columns + ") VALUES(" + placeholders +")";
PreparedStatement stmt = conn.prepareStatement(dml);
for (int row = 0; row < rows.length; row++) {
for (int col = 0; col < rows[row].length; col++) {
Object value = rows[row][col];
stmt.setObject(col + 1, value);
}
stmt.execute();
}
conn.commit();
String selectClause = "SELECT " + appendColumns(projections) + " FROM " + table;
for (WhereCondition whereConditionClause : new WhereCondition[]{whereCondition, WhereCondition.reverse(whereCondition)}) {
String query = WhereCondition.appendWhere(whereConditionClause, selectClause);
query = HavingCondition.appendHaving(havingCondition, query);
query = OrderBy.appendOrderBy(orderBy, query);
runQuery(conn, query, expectedRows);
}
if (orderBy != null) {
orderBy = OrderBy.reverse(orderBy);
String query = WhereCondition.appendWhere(whereCondition, selectClause);
query = HavingCondition.appendHaving(havingCondition, query);
query = OrderBy.appendOrderBy(orderBy, query);
runQuery(conn, query, reverse(expectedRows));
}
} finally {
conn.close();
}
}
private String appendColumns(String[] columnNames) {
String appendedColumns = "";
for (int i = 0; i < columnNames.length; i++) {
appendedColumns += columnNames[i];
if (i < columnNames.length - 1) {
appendedColumns += ",";
}
}
return appendedColumns;
}
private String appendPlaceholders(String[] columnNames) {
String placeholderList = "";
for (int i = 0; i < columnNames.length; i++) {
placeholderList += "?";
if (i < columnNames.length - 1) {
placeholderList += ",";
}
}
return placeholderList;
}
private static void runQuery(Connection connection, String query, Object[][] expectedValues) throws Exception {
PreparedStatement stmt = connection.prepareStatement(query);
ResultSet rs = stmt.executeQuery();
int rowCounter = 0;
while (rs.next()) {
if (rowCounter == expectedValues.length) {
Assert.assertEquals("Exceeded number of expected rows for query" + query, expectedValues.length, rowCounter+1);
}
Object[] cols = new Object[expectedValues[rowCounter].length];
for (int colCounter = 0; colCounter < expectedValues[rowCounter].length; colCounter++) {
cols[colCounter] = rs.getObject(colCounter+1);
}
Assert.assertArrayEquals("Unexpected result for query " + query, expectedValues[rowCounter], cols);
rowCounter++;
}
Assert.assertEquals("Unexpected number of rows for query " + query, expectedValues.length, rowCounter);
}
private static Object[][] reverse(Object[][] rows) {
Object[][] reversedArray = new Object[rows.length][];
System.arraycopy(rows, 0, reversedArray, 0, rows.length);
ArrayUtils.reverse(reversedArray);
return reversedArray;
}
private static Date date(int month, int day, int year) {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.MONTH, month-1);
cal.set(Calendar.DAY_OF_MONTH, day);
cal.set(Calendar.YEAR, year);
cal.set(Calendar.HOUR_OF_DAY, 10);
cal.set(Calendar.MINUTE, 2);
cal.set(Calendar.SECOND, 5);
cal.set(Calendar.MILLISECOND, 101);
Date d = new Date(cal.getTimeInMillis());
return d;
}
private static String[] upsert(String...args) {
return args;
}
private static String[] select(String...args) {
return args;
}
private static BigDecimal bdec(double d) {
return BigDecimal.valueOf(d);
}
private static BigInteger bint(long l) {
return BigInteger.valueOf(l);
}
private static class WhereCondition {
final String lhs;
final String operator;
final String rhs;
WhereCondition(String lhs, String operator, String rhs) {
this.lhs = lhs;
this.operator = operator;
this.rhs = rhs;
}
static WhereCondition reverse(WhereCondition whereCondition) {
if (whereCondition == null) {
return null;
}
if (whereCondition.operator.equalsIgnoreCase("IN") || whereCondition.operator.equalsIgnoreCase("LIKE")) {
return whereCondition;
} else {
return new WhereCondition(whereCondition.rhs, whereCondition.getReversedOperator(), whereCondition.lhs);
}
}
static String appendWhere(WhereCondition whereCondition, String query) {
if (whereCondition == null) {
return query;
}
return query + " WHERE " + whereCondition.lhs + " " + whereCondition.operator + " " + whereCondition.rhs;
}
private String getReversedOperator() {
if (operator.equals("<")) {
return ">";
} else if (operator.equals(">")) {
return "<";
} else if (operator.equals(">=")) {
return "<=";
} else if (operator.equals("<=")) {
return ">=";
} else {
return operator;
}
}
}
private static class HavingCondition {
private String groupby;
private String having;
HavingCondition(String groupby, String having) {
this.groupby = groupby;
this.having = having;
}
static String appendHaving(HavingCondition havingCondition, String query) {
if (havingCondition == null) {
return query;
}
return query + " GROUP BY " + havingCondition.groupby + " HAVING " + havingCondition.having + " ";
}
}
private static class OrderBy {
enum Direction {
ASC, DESC;
Direction reverse() {
if (this == ASC) {
return DESC;
}
return ASC;
}
}
private List<String> columnNames = Lists.newArrayList();
private List<Direction> directions = Lists.newArrayList();
OrderBy() {
}
OrderBy(String columnName, Direction orderBy) {
add(columnName, orderBy);
}
void add(String columnName, Direction direction) {
columnNames.add(columnName);
directions.add(direction);
}
static OrderBy reverse(OrderBy orderBy) {
if (orderBy == null) {
return null;
}
List<Direction> reversedDirections = Lists.newArrayList();
for (Direction dir : orderBy.directions) {
reversedDirections.add(dir.reverse());
}
OrderBy reversedOrderBy = new OrderBy();
reversedOrderBy.columnNames = orderBy.columnNames;
reversedOrderBy.directions = reversedDirections;
return reversedOrderBy;
}
static String appendOrderBy(OrderBy orderBy, String query) {
if (orderBy == null || orderBy.columnNames.isEmpty()) {
return query;
}
query += " ORDER BY ";
for (int i = 0; i < orderBy.columnNames.size(); i++) {
query += orderBy.columnNames.get(i) + " " + orderBy.directions.get(i).toString() + " ";
}
query += " LIMIT 1000 ";
return query;
}
}
}