blob: d3f5335510cf10ead28d427207be775bbe118cff [file] [log] [blame]
/**
* Derby - Class org.apache.derbyTesting.functionTests.tests.lang.OLAPTest
*
* 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.derbyTesting.functionTests.tests.lang;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.TestConfiguration;
/**
* OLAP functionality test.
*
* Please refer to DERBY-581 for more details.
*/
public class OLAPTest extends BaseJDBCTestCase {
private final static String LANG_WINDOW_FUNCTION_CONTEXT_ERROR = "42ZC2";
private final static String NOT_IMPLEMENTED = "0A000";
private final static String LANG_SYNTAX_ERROR = "42X01";
private final static String LANG_COLUMN_NOT_FOUND = "42X04";
public OLAPTest(String name) {
super(name);
}
public static Test makeSuite() {
Test clean = new CleanDatabaseTestSetup(
new BaseTestSuite(OLAPTest.class)) {
protected void decorateSQL(Statement s)
throws SQLException
{
getConnection().setAutoCommit(false);
s.executeUpdate("create table t1 (a int, b int)");
s.executeUpdate("create table t2 (x int)");
s.executeUpdate("create table t3 (y int)");
s.executeUpdate("create table t4 (a int, b int)");
s.executeUpdate("create table t5 (a int, b int)");
s.executeUpdate(
"insert into t1 values (10,100),(20,200)," +
" (30,300),(40,400)," +
" (50,500)");
s.executeUpdate(
"insert into t2 values (1),(2),(3),(4),(5)");
s.executeUpdate(
"insert into t3 values (4),(5),(6),(7),(8)");
s.executeUpdate(
"insert into t4 values (10,100),(20,200)");
s.executeUpdate(
"insert into t5 values (1,1),(2,4),(3,4),(4,4),(5,9)");
getConnection().commit();
}
};
return clean;
}
public static Test suite()
{
BaseTestSuite suite = new BaseTestSuite("OLAPTest");
suite.addTest(makeSuite());
suite.addTest(TestConfiguration.clientServerDecorator(makeSuite()));
return suite;
}
/**
* Main test body
*
* @throws SQLException
*/
public void testBasicOperations()
throws SQLException {
Statement s = createStatement();
/*
* Positive testing of Statements
*/
/*
* Simple queries
*/
ResultSet rs = s.executeQuery("select row_number() over (), t1.* from t1");
String[][] expectedRows = {{"1", "10", "100"}, {"2", "20", "200"}, {"3", "30", "300"}, {"4", "40", "400"}, {"5", "50", "500"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select row_number() over (), t1.* from t1 where a > 30");
expectedRows = new String[][]{{"1", "40", "400"}, {"2", "50", "500"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select row_number() over (), a from t1 where b > 300");
expectedRows = new String[][]{{"1", "40"}, {"2", "50"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select row_number() over () as r, a from t1 where b > 300");
expectedRows = new String[][]{{"1", "40"}, {"2", "50"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* Two instances of row_number columns in the same RCL */
rs = s.executeQuery("select row_number() over (), row_number() over (), b from t1 where b <= 300");
expectedRows = new String[][]{{"1", "1", "100"}, {"2", "2", "200"}, {"3", "3", "300"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* Two instances of row_number columns in the same RCL, reorder columns */
rs = s.executeQuery("select row_number() over (), b, row_number() over (), a from t1 where b < 300 ");
expectedRows = new String[][]{{"1", "100", "1", "10"}, {"2", "200", "2", "20"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* Pushing predicates (... where r ... ) too far cause this join to fail */
rs = s.executeQuery("select row_number() over(),x from t2,t3 where x=y");
expectedRows = new String[][]{{"1", "4"}, {"2", "5"}};
JDBC.assertFullResultSet(rs, expectedRows);
// DERBY-4069: ORDER BY should be applied at the cursor level, that is
// *after* a windowing clause in the. So, with the original ordering
// here, the ROW_NUMBER should come backwards:
/* Ordering */
rs = s.executeQuery("select row_number() over () as r, t1.* from t1 order by b desc");
expectedRows = new String[][]{{"5", "50", "500"},
{"4", "40", "400"},
{"3", "30", "300"},
{"2", "20", "200"},
{"1", "10", "100"}};
JDBC.assertFullResultSet(rs, expectedRows);
// DERBY-4069: ORDER BY should be applied at the cursor level, that is
// *after* a windowing clause in the. So, with the original ordering
// here, the ROW_NUMBER should come backwards:
/* Ordering on a column dropped in projection */
rs = s.executeQuery("select row_number() over () as r, t1.a from t1 order by b desc");
expectedRows = new String[][]{{"5", "50"},
{"4", "40"},
{"3", "30"},
{"2", "20"},
{"1", "10"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* Only expressions in RCL */
rs = s.executeQuery("select row_number() over (), row_number() over (), 2*t1.a from t1");
expectedRows = new String[][]{{"1", "1", "20"}, {"2", "2","40"}, {"3", "3","60"}, {"4", "4", "80"}, {"5", "5", "100"}};
JDBC.assertFullResultSet(rs, expectedRows);
/*
* Subqueries
*/
/* This query returned no rows at one time */
rs = s.executeQuery("select * from (select row_number() over () as r,x from t2,t3 where x=y) s(r,x) where r < 3");
expectedRows = new String[][]{{"1", "4"}, {"2", "5"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select * from (select row_number() over () as r, t1.* from t1) as tr where r < 3");
expectedRows = new String[][]{{"1", "10", "100"}, {"2", "20", "200"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select * from (select row_number() over () as r, t1.* from t1) as tr where r > 3");
expectedRows = new String[][]{{"4", "40", "400"}, {"5", "50", "500"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* Two instances of row_number columns */
rs = s.executeQuery("select row_number() over(), tr.* from (select row_number() over () as r, t1.* from t1) as tr where r > 2 and r < 5");
expectedRows = new String[][]{{"1", "3", "30", "300"}, {"2", "4", "40", "400"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* Two instances of row_number columns, with projection */
rs = s.executeQuery("select row_number() over(), tr.b from (select row_number() over () as r, t1.* from t1) as tr where r > 2 and r < 5");
expectedRows = new String[][]{{"1", "300"}, {"2", "400"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* Column ordering */
rs = s.executeQuery("select * from (select t1.b, row_number() over () as r from t1) as tr where r > 3");
expectedRows = new String[][]{{"400", "4"}, {"500", "5"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* Column ordering with projection*/
rs = s.executeQuery("select b from (select t1.b, row_number() over () as r from t1) as tr where r > 3");
expectedRows = new String[][]{{"400"}, {"500"}};
JDBC.assertFullResultSet(rs, expectedRows);
/*
* Aggregates over window functions once failed
*/
rs = s.executeQuery("select count(*) from (select row_number() over() from t1) x");
expectedRows = new String[][]{{"5"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select count(*) from (select row_number() over () as r from t1) as t(r) where r <=3");
expectedRows = new String[][]{{"3"}};
JDBC.assertFullResultSet(rs, expectedRows);
/*
* Some other joins with window functions.
* Run off a smaller table t4 to reduce expected row count.
*/
rs = s.executeQuery("select row_number() over () from t1 union all select row_number() over () from t1");
expectedRows = new String[][]{{"1"},{"2"},{"3"},{"4"},{"5"},{"1"},{"2"},{"3"},{"4"},{"5"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select 2 * r from (select row_number() over () from t1) x(r)");
expectedRows = new String[][]{{"2"},{"4"},{"6"},{"8"},{"10"},};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select c3, c1, c2 from " +
"(select a, b, row_number() over() as r from t4) x1 (c1, c2, r1), " +
"(select row_number() over() as r, b, a from t4) x2 (r2, c3, c4)");
expectedRows = new String[][]{{"100", "10", "100"},
{"200", "10", "100"},
{"100", "20", "200"},
{"200", "20", "200"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select c3, c1, c2 from " +
"(select a, b, row_number() over() as r from t4) x1 (c1, c2, r1), " +
"(select row_number() over() as r, b, a from t4) x2 (r2, c3, c4), " +
"t4");
expectedRows = new String[][]{{"100", "10", "100"},
{"100", "10", "100"},
{"200", "10", "100"},
{"200", "10", "100"},
{"100", "20", "200"},
{"100", "20", "200"},
{"200", "20", "200"},
{"200", "20", "200"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select c3, c1, c2 from "+
"(select a, b, row_number() over() as r from t4) x1 (c1, c2, r1), "+
"(select row_number() over() as r, b, a from t4) x2 (r2, c3, c4), "+
"t4 "+
"where x1.r1 = 2 * x2.r2");
expectedRows = new String[][]{{"100", "20", "200"}, {"100", "20", "200"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select c3, c1, c2 from "+
"(select a, b, row_number() over() as r from t4) x1 (c1, c2, r1), "+
"(select row_number() over() as r, b, a from t4) x2 (r2, c3, c4), "+
"t4 "+
"where x1.r1 = 2 * x2.r2");
expectedRows = new String[][]{{"100", "20", "200"}, {"100", "20", "200"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* Two problematic joins reported during development */
rs = s.executeQuery("select c3, c1, c2 from "+
"(select a, b, row_number() over() as r from t4) x1 (c1, c2, r1), "+
"(select row_number() over() as r, b, a from t4) x2 (r2, c3, c4), "+
"t4 "+
"where x2.c4 = t4.a");
expectedRows = new String[][]{{"100", "10", "100"},
{"100", "20", "200"},
{"200", "10", "100"},
{"200", "20", "200"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select c3, c1, c2 from "+
"(select a, b, row_number() over() as r from t1) x1 (c1, c2, r1), "+
"(select row_number() over() as r, b, a from t1) x2 (r2, c3, c4), "+
"t1 "+
"where x1.r1 = 2 * x2.r2 and x2.c4 = t1.a");
expectedRows = new String[][]{{"100", "20", "200"}, {"200", "40", "400"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Check that flattening does not happen when a window is used in a
// subquery
rs = s.executeQuery("select * from t5 o where o.a in " +
"(select x + row_number() over () from t2)");
expectedRows = new String[][]{{"2", "4"},
{"4", "4"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Subquery in SELECT list. DERBY-5954
rs = s.executeQuery(
"SELECT rn_t1, (" +
" SELECT rn_t2 FROM (" +
" SELECT row_number() over() as rn_t2 FROM t2) " +
" as T_2" +
" where T_2.rn_t2 = T_1.rn_t1 + 1 ) " +
" as rn_outer" +
" FROM (SELECT row_number() over() as rn_t1 from t2) as T_1");
expectedRows = new String[][]{{"1", "2"},
{"2", "3"},
{"3", "4"},
{"4", "5"},
{"5", null}};
JDBC.assertFullResultSet(rs, expectedRows);
/*
* Group by and having
*/
rs = s.executeQuery("select r from (select a, row_number() over() as r, b from t1) x group by r");
expectedRows = new String[][]{{"1"}, {"2"}, {"3"}, {"4"}, {"5"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select * from (select a, row_number() over() as r, b from t1) x group by a, b, r");
expectedRows = new String[][]{{"10", "1", "100"},
{"20", "2", "200"},
{"30", "3", "300"},
{"40", "4", "400"},
{"50", "5", "500"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select * from (select a, row_number() over() as r, b from t1) x group by b, r, a");
expectedRows = new String[][]{{"10", "1", "100"},
{"20", "2", "200"},
{"30", "3", "300"},
{"40", "4", "400"},
{"50", "5", "500"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select * from "+
"(select a, row_number() over() as r, b from t1) x "+
"group by b, r, a "+
"having r > 2");
expectedRows = new String[][]{{"30", "3", "300"},
{"40", "4", "400"},
{"50", "5", "500"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select * from "+
"(select a, row_number() over() as r, b from t1) x "+
"group by b, r, a "+
"having r > 2 and a >=30 "+
"order by a desc");
expectedRows = new String[][]{{"50", "5", "500"},
{"40", "4", "400"},
{"30", "3", "300"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select * from "+
"(select a, row_number() over() as r, b from t1) x "+
"group by b, r, a "+
"having r > 2 and a >=30 "+
"order by r desc");
expectedRows = new String[][]{{"50", "5", "500"},
{"40", "4", "400"},
{"30", "3", "300"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select * from "+
"(select a, row_number() over() as r, b from t1) x "+
"group by b, r, a "+
"having r > 2 and a >=30 "+
"order by a asc, r desc");
expectedRows = new String[][]{{"30", "3", "300"},
{"40", "4", "400"},
{"50", "5", "500"}};
JDBC.assertFullResultSet(rs, expectedRows);
/* A couple of distinct queries */
rs = s.executeQuery("select distinct row_number() over (), 'ABC' from t1");
expectedRows = new String[][]{{"1", "ABC"},
{"2", "ABC"},
{"3", "ABC"},
{"4", "ABC"},
{"5", "ABC"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery(
"select * from (select distinct row_number() over (), " +
" 'ABC' from t1) tmp");
expectedRows = new String[][]{{"1", "ABC"},
{"2", "ABC"},
{"3", "ABC"},
{"4", "ABC"},
{"5", "ABC"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Test explicitly declared window
rs = s.executeQuery(
"select * from (select distinct row_number() over w, 'ABC' " +
"from t1 window w as ()) tmp");
JDBC.assertFullResultSet(rs, expectedRows);
// DERBY-3634 Cannot use row_number() in ORDER BY clause
rs = s.executeQuery(
"select row_number() over () r, a from t1 order by r desc");
expectedRows = new String[][]{{"5", "50"},
{"4", "40"},
{"3", "30"},
{"2", "20"},
{"1", "10"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery(
"select a from t1 order by row_number() over () desc");
expectedRows = new String[][]{{"50"},
{"40"},
{"30"},
{"20"},
{"10"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Another case mentioned in DERBY-3634
rs = s.executeQuery("select a, row_number() over () from t1 except " +
"select a, row_number() over () from t1");
JDBC.assertEmpty(rs);
// And yet another case mentioned in DERBY-3634 This actually also
// tests that ROW_NUMBER in an ORDER BY does not get optimized away if
// there is a restriction, see SelectNode's call to
// orderByList.removeConstantColumns(wherePredicates).
rs = s.executeQuery("select abs(a), row_number() over () c " +
"from t1 where a > 30 and a <= 50 " +
"order by c desc");
expectedRows = new String[][]{{"50", "2"},
{"40", "1"}};
JDBC.assertFullResultSet(rs, expectedRows);
// DERBY-3635 Cannot build SELECT LIST expressions involving
// ROW_NUMBER()
rs = s.executeQuery("select row_number() over () + 10, a from t1 " +
"order by row_number() over () desc");
expectedRows = new String[][]{{"15", "50"},
{"14", "40"},
{"13", "30"},
{"12", "20"},
{"11", "10"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Check that a non-used window doesn't cause problems
rs = s.executeQuery("select a from t1 window r as () order by a desc");
expectedRows = new String[][]{{"50"},
{"40"},
{"30"},
{"20"},
{"10"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Check that a row_number combined with group by works. Good to test
// this since windowing uses a similar rewrite mechanism to group by
// and could interfere (seen during development).
rs = s.executeQuery("select row_number() over r, b, sum(a) from t5 " +
"group by b window r as ()");
expectedRows = new String[][]{{"1", "1", "1"},
{"2", "4", "9"},
{"3", "9", "5"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Check that a row_number combined with group by works. Good to test
// this since windowing uses a similar rewrite mechanism to group by
// and could interfere (seen during development).
rs = s.executeQuery("select row_number() over r, b, sum(a) from t5 " +
"group by b window r as ()");
expectedRows = new String[][]{{"1", "1", "1"},
{"2", "4", "9"},
{"3", "9", "5"}};
JDBC.assertFullResultSet(rs, expectedRows);
rs = s.executeQuery("select b, sum(a) from t5 " +
"group by b window r as ()");
expectedRows = new String[][]{{"1", "1"},
{"4", "9"},
{"9", "5"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Check that row_number inside EXISTS works.
rs = s.executeQuery("SELECT * FROM t2 WHERE EXISTS " +
"(SELECT ROW_NUMBER() OVER () FROM t5)");
expectedRows = new String[][]{{"1"},{"2"},{"3"},{"4"},{"5"}};
JDBC.assertFullResultSet(rs, expectedRows);
/*
* Negative testing of Statements
*/
// Missing required OVER ()
assertStatementError(
LANG_SYNTAX_ERROR, s,
"select row_number() as r, * from t1 where t1.a > 2");
// Illegal where clause, r not a named column of t1.
assertStatementError(
LANG_COLUMN_NOT_FOUND, s,
"select row_number() over () as r, a from t1 where r < 3");
// Illegal use of asterisk with another column identifier.
assertStatementError(
LANG_SYNTAX_ERROR, s,
"select row_number() over () as r, * from t1 where t1.a > 2");
// Order by in window specification
assertStatementError(
NOT_IMPLEMENTED,
s,
"select row_number() over (order by i) as r from t1");
// Other window function than row_number:
assertStatementError(NOT_IMPLEMENTED,
s,
"select max(i) over () from t1");
// Illegal context: WHERE, cf. SQL 2003, section 7.8 SR 2
assertStatementError(LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"select * from t4 where row_number() over () > 3");
// But nested inside a subquery it should work:
rs = s.executeQuery("select * from t2 where x in " +
" (select row_number() over () from t4)");
expectedRows = new String[][]{{"1"},{"2"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Illegal context: GROUP BY
assertStatementError(LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"select * from t4 group by row_number() over ()");
// But nested inside a subquery it should work.
// Fails: DERBY-4403, enable this test when that issue is fixed.
// rs = s.executeQuery("select * from t4 group by a + " +
// "(select row_number() over () from t4 where a=10)");
// JDBC.assertEmpty(rs);
// Illegal context: HAVING, cf. SQL 2003, section 7.10 SR 4
assertStatementError(
LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"select * from t4 group by a having b = row_number() over ()");
// But nested inside a subquery it should work:
rs = s.executeQuery(
"select sum(a) from t5 group by b " +
" having b = (select row_number() over () + 3 " +
" from t5 where a=1)");
expectedRows = new String[][]{{"9"}};
// Illegal context: VALUES
assertStatementError(LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"values row_number() over ()");
// But nested inside a subquery it should work:
rs = s.executeQuery("values 3 + " +
"(select row_number() over () from t2 where x=1)");
expectedRows = new String[][]{{"4"}};
JDBC.assertFullResultSet(rs, expectedRows);
// Illegal context: Aggregate function, cf. SQL 2003, section 10.9 SR 7
// a)
assertStatementError(LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"select sum(row_number() over ()) from t4");
// Illegal context: JOIN ON clause, cf. SQL 2003, section 7.7 SR 5
assertStatementError(
LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"select * from t4 t_1 join t4 t_2 on " +
" t_1.a = row_number() over () + t_2.a");
// DERBY-6565: NPE before
assertStatementError(
LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"update t3 set y = y - row_number() over ()");
// DERBY-6688: subquery using SubqueryNode rather than FromSubquery
// had problems with presence of window function in order by.
JDBC.assertFullResultSet(s.executeQuery("select * from t3"),
new String[][]{{"4"},{"5"},{"6"},{"7"},{"8"}});
// failed prior to DERBY-6688
s.executeUpdate(
"update t3 set y = y - " +
" (select y from t3 order by row_number() over () " +
" fetch first 1 row only)");
JDBC.assertFullResultSet(s.executeQuery("select * from t3"),
new String[][]{{"0"},{"1"},{"2"},{"3"},{"4"}});
// Used to work before
JDBC.assertFullResultSet(s.executeQuery(
"select * from " +
" (select y from t3 order by row_number() over () " +
" fetch first 1 row only) tt"),
new String[][]{{"0"}});
// failed prior to DERBY-6688
JDBC.assertFullResultSet(s.executeQuery(
"select * from t3 where y = " +
" (select y from t3 order by row_number() over () " +
" fetch first row only)"),
new String[][]{{"0"}});
// DERBY-6689: ArrayIndexOutOfBoundsException before
assertStatementError(LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"merge into t2 using t3 on (t2.x=t3.y) when not matched then " +
" insert values (row_number() over ())");
// DERBY-6691: NPE before
assertStatementError(LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"call syscs_util.syscs_compress_table(" +
" 'APP', 'T2', row_number() over ())");
// DERBY-6690: a window function in generated clause was not detected
// before
assertStatementError(LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"create table t (x int generated always as " +
" (row_number() over ()))");
assertStatementError(LANG_WINDOW_FUNCTION_CONTEXT_ERROR,
s,
"alter table t2 add column foo int generated always as " +
" (row_number() over ())");
}
public void testMetaData()
throws SQLException {
if (JDBC.vmSupportsJSR169()) {
// does not support metadata
return;
}
Statement s = createStatement();
ResultSet rs = s.executeQuery(
"select row_number() over () from sys.systables");
ResultSetMetaData rsmd = rs.getMetaData();
assertEquals(ResultSetMetaData.columnNoNulls, rsmd.isNullable(1));
}
private String makeString(int len)
{
StringBuffer buf = new StringBuffer(len);
for (int i = 0; i < len; i++)
buf.append('a');
return buf.toString();
}
/**
* Basic test of GROUP BY ROLLUP capability.
*
* This test case has a few basic tests of GROUP BY ROLLUP, both
* positive and negative tests.
*/
public void testGroupByRollup()
throws SQLException
{
Statement s = createStatement();
s.executeUpdate("create table ru (a int, b int, c int, d int)");
s.executeUpdate("insert into ru values (1,1,1,1), (1,2,3,4),"+
"(1,1,2,2), (4,3,2,1), (4,4,4,4)");
JDBC.assertUnorderedResultSet( s.executeQuery(
"select a,b,c,sum(d) from ru group by rollup(a,b,c)"),
new String[][]{
{"1","1","1","1"},
{"1","1","2","2"},
{"1","2","3","4"},
{"4","3","2","1"},
{"4","4","4","4"},
{"1","1",null,"3"},
{"1","2",null,"4"},
{"4","3",null,"1"},
{"4","4",null,"4"},
{"1",null,null,"7"},
{"4",null,null,"5"},
{null,null,null,"12"}});
JDBC.assertFullResultSet( s.executeQuery(
"select count(*) from ru group by mod(a,b)"),
new String[][]{ {"3"},{"2"}});
// Try a few negative tests:
assertStatementError("42X04", s,
"select a,b,c,sum(d) from ru group by rollup");
assertStatementError("42X01", s,
"select a,b,c,sum(d) from ru group by rollup(");
assertStatementError("42X01", s,
"select a,b,c,sum(d) from ru group by rollup)");
assertStatementError("42X01", s,
"select a,b,c,sum(d) from ru group by rollup()");
s.executeUpdate("drop table ru");
s.close();
}
/**
* Verify that ROLLUP can still be used as the name of a column or table.
*/
public void testRollupReservedWord()
throws SQLException
{
Statement s = createStatement();
s.executeUpdate("create table t_roll(rollup int, x int)");
JDBC.assertEmpty( s.executeQuery(
"select rollup, sum(x) from t_roll group by rollup"));
JDBC.assertEmpty( s.executeQuery(
"select count(*) from t_roll group by mod(rollup,x)"));
JDBC.assertEmpty( s.executeQuery(
"select count(*) from t_roll group by mod(x,rollup)"));
s.executeUpdate("create table rollup(a int, x int)");
JDBC.assertEmpty( s.executeQuery("select a, x from rollup"));
s.executeUpdate("insert into rollup(a,x) values(1,2)");
JDBC.assertUnorderedResultSet( s.executeQuery(
"select a,sum(x) from rollup group by rollup(a)"),
new String[][]{
{"1","2"}, {null,"2"}});
s.executeUpdate("drop table rollup");
s.executeUpdate("drop table t_roll");
s.close();
}
/**
* Verify that non-aggregate columns are returned as NULLABLE if ROLLUP.
*
* If a GROUP BY ROLLUP is used, the un-aggregated columns may contain
* NULL values, so we need to verify that the DatabaseMetadata returns
* the right values for the nullability of the columns.
*/
public void testRollupColumnNullability()
throws SQLException
{
Statement s = createStatement();
s.executeUpdate(
"create table t_notnull(a int not null, b int," +
" c int not null, d int)");
ResultSet rs = s.executeQuery(
"select a,b,c,sum(d) from t_notnull group by rollup(a,b,c)");
JDBC.assertNullability(rs,
new boolean[]{true, true, true, true});
rs.close();
rs = s.executeQuery(
"select 1,2,3,sum(d) from t_notnull group by rollup(1,2,3)");
JDBC.assertNullability(rs,
new boolean[]{true, true, true, true});
rs.close();
s.executeUpdate("drop table t_notnull");
s.close();
}
/**
* Verify the behavior of GROUP BY ROLLUP for empty result sets.
*/
public void testRollupEmptyTables()
throws SQLException
{
Statement s = createStatement();
s.executeUpdate("create table ru (a int, b int, c int, d int)");
JDBC.assertEmpty( s.executeQuery("select sum(a) from ru group by b"));
JDBC.assertSingleValueResultSet(
s.executeQuery("select sum(a) from ru"), (String)null);
s.executeUpdate("insert into ru values (1,1,1,1), (1,2,3,4),"+
"(1,1,2,2), (4,3,2,1), (4,4,4,4)");
JDBC.assertEmpty( s.executeQuery(
"select b, sum(a) from ru where 1<>1 group by rollup(b)"));
s.executeUpdate("drop table ru");
s.close();
}
/**
* A ROLLUP case suggested by Dag in 1-sep-2009 comment on DERBY-3002
*/
public void testRollupNullabilityCasts()
throws SQLException
{
Statement s = createStatement();
s.executeUpdate("create table t(c varchar(2) not null," +
"c2 varchar(2), i integer)");
s.executeUpdate("insert into t values('aa',null,null)");
s.executeUpdate("insert into t values('bb',null,null)");
String [][]rows =
new String[][]{
{"aa",null,null},
{"aa",null,null},
{null,null,null},
{"bb",null,null},
{"bb",null,null}};
JDBC.assertUnorderedResultSet(
s.executeQuery("select c,c2,sum(i) from t group by rollup(c,c2)"),
rows);
JDBC.assertUnorderedResultSet(s.executeQuery(
"select cast(c as varchar(2)),c2,sum(i) from t " +
"group by rollup(c,c2)"),
rows);
/* FIXME -- this test currently fails due to improper handling of
the nullability of the result columns.
JDBC.assertUnorderedResultSet(s.executeQuery(
"select cast(x as varchar(2)),y,z from " +
" (select c,c2,sum(i) from t " +
" group by rollup (c,c2)) t(x,y,z)"),
rows);
*/
s.executeUpdate("drop table t");
s.close();
}
/**
* Verify the behavior of GROUP BY ROLLUP when it can use a covering index.
*/
public void testRollupOfCoveringIndex()
throws SQLException
{
Statement s = createStatement();
s.executeUpdate("create table ru (a int,b int,c int,d varchar(1000))");
s.executeUpdate("create index ru_idx on ru(a,b,c)");
PreparedStatement ps = prepareStatement(
"insert into ru (a,b,c,d) values (?,?,?,?)");
for (int i = 0; i < 100; i++)
{
ps.setInt(1, (i%5));
ps.setInt(2, 2*i);
ps.setInt(3, 100+i);
ps.setString(4, makeString(900));
ps.executeUpdate();
}
ps.close();
// FIXME
//dumpIt(s, 2, "select a,sum(c) from ru group by a");
//dumpIt(s, 3, "select a,b,sum(c) from ru group by a,b");
//dumpIt(s, 3, "select a,b,sum(c) from ru group by rollup(a,b)");
s.executeUpdate("drop table ru");
s.close();
}
private void dumpIt(Statement s, int cols, String sql)
throws SQLException
{
System.out.println(sql);
ResultSet rs = s.executeQuery(sql);
while (rs.next())
{
StringBuffer buf = new StringBuffer();
for (int i = 1; i <= cols; i++)
{
if (i > 1)
buf.append(",");
buf.append(rs.getString(i));
}
System.out.println(buf.toString());
}
rs.close();
}
/*
* Various GROUP BY tests, with and without ROLLUP.
*/
public void testGroupByWithAndWithoutRollup()
throws SQLException
{
Statement s = createStatement();
// A very simple set of master-detail ORDER and ORDER_ITEM tables,
// with some fake customer data:
s.executeUpdate(
"create table orders(order_id int primary key," +
" customer varchar(10)," +
" order_date date, " +
" shipping int)");
s.executeUpdate(
"create table order_items(item_id int primary key," +
" order_id int," +
" order_item varchar(10), " +
" cost int)");
s.executeUpdate(
"create table customers(customer varchar(10) primary key," +
" name varchar(100), city varchar(100), state varchar(2))");
s.executeUpdate("insert into customers values " +
"('ABC','ABC Corporation','ABC City', 'AB')," +
"('DEF','DEF, Inc.', 'DEFburg', 'DE')");
s.executeUpdate("insert into orders values(1,'ABC','2009-01-01',40)");
s.executeUpdate("insert into orders values(2,'ABC','2009-01-02',30)");
s.executeUpdate("insert into orders values(3,'ABC','2009-01-03',25)");
s.executeUpdate("insert into orders values(4,'DEF','2009-01-02',10)");
s.executeUpdate("insert into order_items values(1,1,'Item A',100)");
s.executeUpdate("insert into order_items values(2,1,'Item B',150)");
s.executeUpdate("insert into order_items values(3,2,'Item C',125)");
s.executeUpdate("insert into order_items values(4,2,'Item B',50)");
s.executeUpdate("insert into order_items values(5,2,'Item H',200)");
s.executeUpdate("insert into order_items values(6,3,'Item X',100)");
s.executeUpdate("insert into order_items values(7,4,'Item Y',50)");
s.executeUpdate("insert into order_items values(8,4,'Item Z',300)");
// Joining the two tables produces one row per order item:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.order_id, o.customer, o.order_date, " +
"o.shipping, od.item_id, od.order_item, od.cost " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id"),
new String[][]{
{"1","ABC","2009-01-01","40","1","Item A","100"},
{"1","ABC","2009-01-01","40","2","Item B","150"},
{"2","ABC","2009-01-02","30","3","Item C","125"},
{"2","ABC","2009-01-02","30","4","Item B","50"},
{"2","ABC","2009-01-02","30","5","Item H","200"},
{"3","ABC","2009-01-03","25","6","Item X","100"},
{"4","DEF","2009-01-02","10","7","Item Y","50"},
{"4","DEF","2009-01-02","10","8","Item Z","300"},
});
// Grouping the items by customer to compute items/customer:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by o.customer"),
new String[][]{
{"ABC","6"},
{"DEF","2"},
});
// Also include the total cost per customer:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer, " +
" sum(od.cost) as order_total " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by o.customer"),
new String[][]{
{"ABC","6","725"},
{"DEF","2","350"},
});
// ROLLUP the items and costs to grand totals:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer, " +
" sum(od.cost) as order_total " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by ROLLUP(o.customer)"),
new String[][]{
{"ABC","6","725"},
{"DEF","2","350"},
{null,"8","1075"},
});
// Show a usage of Count(distinct) to compute the orders/customer,
// which is not the same as the items/customer:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer, " +
" count(distinct o.order_id) as orders_per_customer " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by o.customer"),
new String[][]{
{"ABC","6","3"},
{"DEF","2","1"},
});
// ROLLUP should work for the distinct count, too:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer, " +
" count(distinct o.order_id) as orders_per_customer " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by ROLLUP(o.customer)"),
new String[][]{
{"ABC","6","3"},
{"DEF","2","1"},
{null,"8","4"},
});
// can we compute the total shipping per customer:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer, " +
" sum(od.cost) as order_total, " +
" count(distinct o.order_id) as orders_per_customer, " +
" sum(o.shipping) as shipping_per_customer " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by o.customer"),
new String[][]{
{"ABC","6","725","3","195"},
{"DEF","2","350","1","20"},
});
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer, " +
" sum(od.cost) as order_total, " +
" count(distinct o.order_id) as orders_per_customer, " +
" sum(o.shipping) as shipping_per_customer " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by ROLLUP(o.customer)"),
new String[][]{
{"ABC","6","725","3","195"},
{"DEF","2","350","1","20"},
{null,"8","1075","4","215"},
});
// Show a usage of distinct shipping aggregate, similar to the
// distinct count aggregate:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer, " +
" sum(od.cost) as order_total, " +
" count(distinct o.order_id) as orders_per_customer, " +
" sum(distinct o.shipping) as shipping_per_customer " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by o.customer"),
new String[][]{
{"ABC","6","725","3","95"},
{"DEF","2","350","1","10"},
});
// Demonstrate some of the dangers of using distinct aggregates.
// Duplicate SUM values may be real duplicates from the data, not
// from duplicate-producing master-detail joins. The COUNT changes
// from 1 to 2 for customer DEF, but the shipping_per_customer is
// still 10, which is logically wrong (there are 2 DEF orders, each
// with value 10, so we "expected" 20 for shipping_per_customer).
s.executeUpdate("insert into orders values(5,'DEF','2009-01-04',10)");
s.executeUpdate("insert into order_items values(9,5,'Item J',125)");
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer, " +
" sum(od.cost) as order_total, " +
" count(distinct o.order_id) as orders_per_customer, " +
" sum(distinct o.shipping) as shipping_per_customer " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by o.customer"),
new String[][]{
{"ABC","6","725","3","95"},
{"DEF","3","475","2","10"},
});
// Same as before, but with ROLLUP:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.Customer, count(*) as items_per_customer, " +
" sum(od.cost) as order_total, " +
" count(distinct o.order_id) as orders_per_customer, " +
" sum(distinct o.shipping) as shipping_per_customer " +
" from orders o inner join order_items od " +
" on o.order_id = od.order_id " +
" group by ROLLUP(o.customer)"),
new String[][]{
{"ABC","6","725","3","95"},
{"DEF","3","475","2","10"},
{null,"9","1200","5","105"},
});
// Produce the results we expected by constructing a sub-query:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select order_id, count(*) as Items_per_order, " +
" sum(cost) as Order_total "+
" from order_items " +
" group by order_id"),
new String[][]{
{"1","2","250"},
{"2","3","375"},
{"3","1","100"},
{"4","2","350"},
{"5","1","125"},
});
JDBC.assertUnorderedResultSet( s.executeQuery(
"select order_id, count(*) as Items_per_order, " +
" sum(cost) as Order_total "+
" from order_items " +
" group by ROLLUP(order_id)"),
new String[][]{
{"1","2","250"},
{"2","3","375"},
{"3","1","100"},
{"4","2","350"},
{"5","1","125"},
{null,"9","1200"},
});
// ... then encapsulate that sub-select with a join to the orders:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.order_id, o.Customer, o.Shipping, " +
" d.items_per_order, d.order_total " +
" from orders o inner join (" +
" select order_id, count(*) as Items_per_order, " +
" sum(cost) as Order_total "+
" from order_items " +
" group by order_id " +
" ) d on o.order_id = d.order_id"),
new String[][]{
{"1","ABC","40","2","250"},
{"2","ABC","30","3","375"},
{"3","ABC","25","1","100"},
{"4","DEF","10","2","350"},
{"5","DEF","10","1","125"},
});
// ... and group *THAT* join, in turn, by customer, to get the
// correct values of shipping_per_customer and items_per_customer.
// Note that total_per_customer is a SUM(SUM()), while
// items_per_customer is a SUM(COUNT()). And no DISTINCT needed.
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.customer, count(*) as orders_per_customer, " +
" sum(o.shipping) as shipping_per_customer, " +
" sum(d.items_per_order) as items_per_customer, " +
" sum(d.order_total) as total_per_customer " +
" from orders o inner join (" +
" select order_id, count(*) as Items_per_order, " +
" sum(cost) as Order_total "+
" from order_items " +
" group by order_id " +
" ) d on o.order_id = d.order_id " +
" group by o.customer"),
new String[][]{
{"ABC","3","95","6","725"},
{"DEF","2","20","3","475"},
});
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.customer, count(*) as orders_per_customer, " +
" sum(o.shipping) as shipping_per_customer, " +
" sum(d.items_per_order) as items_per_customer, " +
" sum(d.order_total) as total_per_customer " +
" from orders o inner join (" +
" select order_id, count(*) as Items_per_order, " +
" sum(cost) as Order_total "+
" from order_items " +
" group by order_id " +
" ) d on o.order_id = d.order_id " +
" group by ROLLUP(o.customer)"),
new String[][]{
{"ABC","3","95","6","725"},
{"DEF","2","20","3","475"},
{null,"5","115","9","1200"},
});
// Include customer address information. First by joining and grouping:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select o.customer, c.name, c.city, c.state, " +
" count(*) as orders_per_customer, " +
" sum(o.shipping) as shipping_per_customer, " +
" sum(d.items_per_order) as items_per_customer, " +
" sum(d.order_total) as total_per_customer " +
" from orders o inner join (" +
" select order_id, count(*) as Items_per_order, " +
" sum(cost) as Order_total "+
" from order_items " +
" group by order_id " +
" ) d on o.order_id = d.order_id " +
" inner join customers c on o.customer = c.customer " +
" group by ROLLUP(o.customer,c.name, c.city,c.state)"),
new String[][]{
{"ABC","ABC Corporation","ABC City","AB","3","95","6","725"},
{"DEF","DEF, Inc.","DEFburg","DE","2","20","3","475"},
{"ABC","ABC Corporation","ABC City",null,"3","95","6","725"},
{"DEF","DEF, Inc.","DEFburg",null,"2","20","3","475"},
{"ABC","ABC Corporation",null,null,"3","95","6","725"},
{"DEF","DEF, Inc.",null,null,"2","20","3","475"},
{"ABC",null,null,null,"3","95","6","725"},
{"DEF",null,null,null,"2","20","3","475"},
{null,null,null,null,"5","115","9","1200"},
});
// Then, alternately, by sub-selecting and grouping:
JDBC.assertUnorderedResultSet( s.executeQuery(
"select c.customer, c.name, c.city, c.state, " +
" o.orders_per_customer, o.shipping_per_customer, " +
" o.items_per_customer, o.total_per_customer " +
" from ( " +
" select o.customer, count(*) as orders_per_customer, " +
" sum(o.shipping) as shipping_per_customer, " +
" sum(d.items_per_order) as items_per_customer, " +
" sum(d.order_total) as total_per_customer " +
" from orders o inner join (" +
" select order_id, count(*) as Items_per_order, " +
" sum(cost) as Order_total "+
" from order_items " +
" group by order_id " +
" ) d on o.order_id = d.order_id " +
" group by o.customer) o " +
" inner join customers c on o.customer = c.customer"),
new String[][]{
{"ABC","ABC Corporation","ABC City","AB","3","95","6","725"},
{"DEF","DEF, Inc.","DEFburg","DE","2","20","3","475"},
});
// Note that we can put the ROLLUP in the sub-query, but then we
// need to outer-join with the customers table since the rollup
// results will have NULL in the join key.
JDBC.assertUnorderedResultSet( s.executeQuery(
"select c.customer, c.name, c.city, c.state, " +
" o.orders_per_customer, o.shipping_per_customer, " +
" o.items_per_customer, o.total_per_customer " +
" from ( " +
" select o.customer, count(*) as orders_per_customer, " +
" sum(o.shipping) as shipping_per_customer, " +
" sum(d.items_per_order) as items_per_customer, " +
" sum(d.order_total) as total_per_customer " +
" from orders o inner join (" +
" select order_id, count(*) as Items_per_order, " +
" sum(cost) as Order_total "+
" from order_items " +
" group by order_id " +
" ) d on o.order_id = d.order_id " +
" group by ROLLUP(o.customer)) o " +
" left outer join customers c on o.customer = c.customer"),
new String[][]{
{"ABC","ABC Corporation","ABC City","AB","3","95","6","725"},
{"DEF","DEF, Inc.","DEFburg","DE","2","20","3","475"},
{null,null,null,null,"5","115","9","1200"},
});
s.close();
}
}