blob: 48557be16cb2552e10d50324d58e389582362288 [file] [log] [blame]
/*
* Class org.apache.derbyTesting.functionTests.tests.lang.OrderByAndOffsetFetchInSubqueries
*
* 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.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.RuntimeStatisticsParser;
import org.apache.derbyTesting.junit.SQLUtilities;
import org.apache.derbyTesting.junit.TestConfiguration;
/**
* Tests for DERBY-4397 Allow {@code ORDER BY} in subqueries
* and DERBY-4398 Allow {@code OFFSET/FETCH} in subqueries.
*/
public class OrderByAndOffsetFetchInSubqueries extends BaseJDBCTestCase {
final static String SYNTAX_ERROR = "42X01";
final static String COLUMN_NOT_FOUND = "42X04";
final static String COLUMN_OUT_OF_RANGE = "42X77";
final static String ORDER_BY_COLUMN_NOT_FOUND = "42X78";
public OrderByAndOffsetFetchInSubqueries(String name) {
super(name);
}
/**
* Construct top level suite in this JUnit test
*
* @return A suite containing embedded and client suites.
*/
public static Test suite()
{
BaseTestSuite suite =
new BaseTestSuite("OrderByAndOffsetFetchInSubqueries");
suite.addTest(makeSuite());
suite.addTest(
TestConfiguration.clientServerDecorator(makeSuite()));
return suite;
}
/**
* Construct suite of tests
*
* @return A suite containing the test cases.
*/
private static Test makeSuite()
{
return new CleanDatabaseTestSetup(
new BaseTestSuite(OrderByAndOffsetFetchInSubqueries.class)) {
@Override
protected void decorateSQL(Statement s)
throws SQLException {
getConnection().setAutoCommit(false);
s.execute("create table temp1(s varchar(10))");
// GENERATED ALWAYS AS IDENTITY
s.execute("create table temp2(" +
"i integer not null " +
" generated always as identity," +
"s varchar(10))");
s.execute("create table temp2b(" +
"i integer not null " +
" generated always as identity," +
"s varchar(10))");
// DEFAULT value
s.execute("create table temp3(" +
"i integer not null " +
" generated always as identity," +
"s varchar(10)," +
"j integer not null " +
" default 66," +
"t varchar(10))");
// GENERATED ALWAYS AS (expression)
s.execute("create table temp4(" +
"i integer not null " +
" generated always as identity," +
"s varchar(10)," +
"j integer not null " +
" generated always as (2*i)," +
"t varchar(10))");
s.execute("create table t01(c1 int)");
s.execute("create table t02(c2 int)");
s.execute("create table t_source(c1 int, c2 varchar(10))");
s.execute("create table t(i int not null, " +
" constraint c unique (i), " +
" j int, k int)");
getConnection().commit();
}
};
}
/**
* Test {@code INSERT INTO t SELECT .. FROM .. ORDER BY}.
*
* @throws java.sql.SQLException
*/
public void testInsertSelectOrderBy() throws SQLException {
//
// Shows that DERBY-4 is now solved.
//
setAutoCommit(false);
Statement s = createStatement();
ResultSet rs;
s.execute("insert into temp1 values 'x','a','c','b','a'");
s.execute("insert into temp2(s) select s from temp1 order by s");
s.execute("insert into temp2(s) select s as a1 from temp1 order by a1");
s.execute("insert into temp2(s) select * from temp1 order by s");
rs = s.executeQuery("select * from temp2");
JDBC.assertFullResultSet(rs, new String[][]{
{"1", "a"},
{"2", "a"},
{"3", "b"},
{"4", "c"},
{"5", "x"},
{"6", "a"},
{"7", "a"},
{"8", "b"},
{"9", "c"},
{"10", "x"},
{"11", "a"},
{"12", "a"},
{"13", "b"},
{"14", "c"},
{"15", "x"}});
rs = s.executeQuery("select * from temp2 order by i");
JDBC.assertFullResultSet(rs, new String[][]{
{"1", "a"},
{"2", "a"},
{"3", "b"},
{"4", "c"},
{"5", "x"},
{"6", "a"},
{"7", "a"},
{"8", "b"},
{"9", "c"},
{"10", "x"},
{"11", "a"},
{"12", "a"},
{"13", "b"},
{"14", "c"},
{"15", "x"}});
s.execute("insert into temp2(s) select s as a1 from temp1 order by s");
// This should be rejected as "no such column" errors:
assertStatementError(
COLUMN_NOT_FOUND, s,
"insert into temp2(s) select s as a1 from temp1 order by no_such");
// A similar example, but with integers rather than strings, and some
// intermediate select statements to show that the ordering is working.
//
s.execute("insert into t01 values (50), (10), (1000), (15), (51)");
rs = s.executeQuery("select * from t01");
JDBC.assertFullResultSet(rs, new String[][]{
{"50"},
{"10"},
{"1000"},
{"15"},
{"51"}});
s.execute("insert into t02 select * from t01 order by c1");
s.execute("insert into t02 select * from t01");
s.execute("insert into t02 select * from t01 order by c1");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"10"},
{"15"},
{"50"},
{"51"},
{"1000"},
{"50"},
{"10"},
{"1000"},
{"15"},
{"51"},
{"10"},
{"15"},
{"50"},
{"51"},
{"1000"}});
// Combining ORDER BY and VALUES is not legal SQL, cf. SQL 2008,
// section 14.11, Syntactic Rule 17: "A <query expression> simply
// contained in a <from subquery> shall not be a <table value
// constructor>. See also discussion in JIRA on DERBY-4413
// (2009-OCT-23).
//
assertStatementError(
SYNTAX_ERROR, s,
"insert into t02 values 66 order by 1");
assertStatementError(
SYNTAX_ERROR, s,
"insert into t02 values (901), (920), (903) order by 1");
// But this should work:
s.executeUpdate("delete from t02");
s.executeUpdate("insert into t02 select 900 from sys.systables " +
" union values 66 order by 1");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"66"},
{"900"}});
// other way around:
s.executeUpdate("delete from t02");
s.executeUpdate(
"insert into t02 values 66 " +
" union select 900 from sys.systables order by 1");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"66"},
{"900"}});
// and, somewhat perversely (since a plain "values 66 order by 1" is
// illegal), this:
s.executeUpdate("delete from t02");
s.executeUpdate("insert into t02 values 66 " +
" union values 66 order by 1");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"66"}});
// UNION
//
// ok:
s.execute("delete from t02");
s.execute("insert into t02 select * from t01 union all " +
" select * from t01 order by c1");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"10"},
{"10"},
{"15"},
{"15"},
{"50"},
{"50"},
{"51"},
{"51"},
{"1000"},
{"1000"}});
// Not ok (c1 is not a column in the union result set, since t02 has
// column c02.
assertStatementError(
ORDER_BY_COLUMN_NOT_FOUND, s,
"insert into t02 select * from t01 union all " +
" select * from t02 order by c1");
// Complication: project away sort column
s.execute("delete from t02");
s.execute("insert into t_source " +
" values (1, 'one'), (2, 'two'), (8, 'three')");
s.execute("insert into t_source(c1) " +
" select c1 from t_source order by c2 desc");
rs = s.executeQuery("select * from t_source");
JDBC.assertFullResultSet(rs, new String[][]{
{"1", "one"},
{"2", "two"},
{"8", "three"},
{"2", null},
{"8", null},
{"1", null}});
// DERBY-4496
s.executeUpdate("create table t4496(x varchar(100))");
s.execute("insert into t4496(x) select ibmreqd from " +
" (select * from sysibm.sysdummy1" +
" order by length(ibmreqd)) t1");
JDBC.assertFullResultSet(
s.executeQuery("select * from t4496"),
new String[][]{{"Y"}});
// DERBY-6006. INSERT INTO ... SELECT FROM could fail with a
// NullPointerException in insane builds, or XSCH5 or assert in sane
// builds, if the SELECT had an ORDER BY column that was not referenced
// in the select list, and if normalization was required because the
// types in the select list didn't exactly match the types in the
// target table.
//
// In the test case below, the select list has an INT (the literal 1),
// whereas the target type is DOUBLE. Also, the ORDER BY column (X) is
// not in the select list.
s.execute("create table t6006(x double)");
assertUpdateCount(s, 6, "insert into t6006 values 1,2,3,4,5,6");
assertUpdateCount(s, 6,
"insert into t6006 select 1 from t6006 order by x");
rollback();
}
/**
* Same test as {@code testInsertSelectOrderBy} but with use of
* {@code OFFSET/FETCH FIRST}.
* <p/>
* Test {@code INSERT INTO t SELECT .. FROM .. ORDER BY} + {@code OFFSET
* FETCH}
* <p/>
* This test is a variant made my modifying {@code testInsertSelectOrderBy}
* with suitable {@code OFFSET/FETCH FIRST} clauses.
*
* @throws java.sql.SQLException
*/
public void testInsertSelectOrderByOffsetFetch() throws SQLException {
//
// Shows that DERBY-4 is now solved.
//
setAutoCommit(false);
Statement s = createStatement();
s.execute("insert into temp1 values 'x','a','c','b','a'");
s.execute("insert into temp2b(s) select s from temp1 order by s " +
" offset 1 rows fetch next 4 rows only");
JDBC.assertFullResultSet(
s.executeQuery("select * from temp2b"),
new String[][]{
{"1", "a"},
{"2", "b"},
{"3", "c"},
{"4", "x"}});
s.execute(
"insert into temp2b(s) select s as a1 from temp1 order by a1" +
" offset 1 rows fetch next 4 rows only");
s.execute(
"insert into temp2b(s) select * from temp1 order by s " +
" offset 1 rows fetch next 4 rows only");
ResultSet rs = s.executeQuery("select * from temp2b");
JDBC.assertFullResultSet(rs, new String[][]{
{"1", "a"},
{"2", "b"},
{"3", "c"},
{"4", "x"},
{"5", "a"},
{"6", "b"},
{"7", "c"},
{"8", "x"},
{"9", "a"},
{"10", "b"},
{"11", "c"},
{"12", "x"}});
rs = s.executeQuery("select * from temp2b order by i");
JDBC.assertFullResultSet(rs, new String[][]{
{"1", "a"},
{"2", "b"},
{"3", "c"},
{"4", "x"},
{"5", "a"},
{"6", "b"},
{"7", "c"},
{"8", "x"},
{"9", "a"},
{"10", "b"},
{"11", "c"},
{"12", "x"}});
// A similar example, but with integers rather than strings, and some
// intermediate select statements to show that the ordering is working.
//
s.execute("insert into t01 values (50), (10), (1000), (15), (51)");
rs = s.executeQuery("select * from t01");
JDBC.assertFullResultSet(rs, new String[][]{
{"50"},
{"10"},
{"1000"},
{"15"},
{"51"}});
s.execute(
"insert into t02 select * from t01 order by c1 " +
" fetch first 2 rows only");
s.execute(
"insert into t02 select * from t01");
s.execute(
"insert into t02 select * from t01 order by c1 offset 0 rows");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"10"},
{"15"},
{"50"},
{"10"},
{"1000"},
{"15"},
{"51"},
{"10"},
{"15"},
{"50"},
{"51"},
{"1000"}});
// Illegal context
assertStatementError(
SYNTAX_ERROR, s,
"insert into t02 values 66 offset 1 row");
// But this should work:
s.executeUpdate("delete from t02");
s.executeUpdate(
"insert into t02 select 900 from sys.systables " +
" union values 66 order by 1 offset 1 row");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"900"}});
// other way around:
s.executeUpdate("delete from t02");
s.executeUpdate(
"insert into t02 values 66 " +
" union select 900 from sys.systables fetch next 1 row only");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"66"}});
s.executeUpdate("delete from t02");
s.executeUpdate("insert into t02 select * from (values 3,4,5 )v " +
" order by 1 offset 1 row fetch next 2 rows only");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"4"},
{"5"}});
// UNION
//
// ok:
s.execute("delete from t02");
s.execute("insert into t02 select * from t01 union all " +
" select * from t01 order by c1 " +
" fetch next 4 rows only");
rs = s.executeQuery("select * from t02");
JDBC.assertFullResultSet(rs, new String[][]{
{"10"},
{"10"},
{"15"},
{"15"}});
// EXCEPT
s.execute("delete from t01");
s.execute("insert into t02 values 6,7");
s.execute("insert into t01 select * from t02 except " +
" values 10 order by 1 offset 1 row");
rs = s.executeQuery("select * from t01");
JDBC.assertFullResultSet(rs, new String[][]{
{"7"},
{"15"}});
// Complication: project away sort column
s.execute("delete from t02");
s.execute("insert into t_source " +
" values (1, 'one'), (2, 'two'), (8, 'three')");
s.execute("insert into t_source(c1) " +
" select c1 from t_source order by c2 desc " +
" fetch next 2 rows only");
rs = s.executeQuery("select * from t_source");
JDBC.assertFullResultSet(rs, new String[][]{
{"1", "one"},
{"2", "two"},
{"8", "three"},
{"2", null},
{"8", null}});
rollback();
}
/**
* {@code SELECT} subqueries with {@code ORDER BY}
*
* @throws java.sql.SQLException
*/
public void testSelectSubqueriesOrderBy() throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
ResultSet rs;
s.execute(
"insert into t_source values (1, 'one'), (2, 'two'), (8, 'three')");
/*
* Simple SELECT FromSubquery
*/
rs = s.executeQuery(
"select * from (select c1 from t_source order by c1 desc) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"8"}, {"2"}, {"1"}});
rs = s.executeQuery(
"select * from (select c1+1 from t_source order by c1+1 desc) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"9"}, {"3"}, {"2"}});
rs = s.executeQuery(
"select * from (select c1,c2 from t_source order by c1 desc,2) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"8", "three"}, {"2", "two"}, {"1", "one"}});
// Complication: project away sort column
rs = s.executeQuery(
"select * from (select c2 from t_source order by c1 desc) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"three"}, {"two"}, {"one"}});
rs = s.executeQuery(
"select * from " +
" (select c2 from t_source order by c1 desc) s order by 1");
JDBC.assertFullResultSet(rs, new String[][]{
{"one"}, {"three"}, {"two"}});
/*
* Simple VALUES FromSubquery
*/
rs = s.executeQuery(
"select * from (values (1, 'one'), (2, 'two'), (8, 'three')" +
" order by 1 desc) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"8", "three"}, {"2", "two"}, {"1", "one"}});
/*
* ORDER BY in EXISTS subquery
*/
rs = s.executeQuery(
"select c1 from t_source where exists " +
" (select c1 from t_source order by c1)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"2"}, {"8"}});
rs = s.executeQuery(
"select c1 from t_source where exists " +
" (select c1 from t_source order by c1 desc)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"2"}, {"8"}});
rs = s.executeQuery(
"select c1 from t_source where exists " +
" (select c1 from t_source order by c2 desc)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"2"}, {"8"}});
rs = s.executeQuery(
"select c1 from t_source where exists " +
" (select c1 from t_source order by c2 desc) order by 1 desc");
JDBC.assertFullResultSet(rs, new String[][]{
{"8"}, {"2"}, {"1"}});
/*
* NOT EXISTS
*/
rs = s.executeQuery(
"select c1 from t_source where not exists " +
" (select c1 from t_source order by c2 desc) order by 1 desc");
JDBC.assertEmpty(rs);
rs = s.executeQuery(
"select c1 from t_source ot where not exists " +
" (select c1 from t_source where ot.c1=(c1/2) order by c2 desc)" +
" order by 1 desc");
JDBC.assertFullResultSet(rs, new String[][]{
{"8"}, {"2"}});
/*
* IN subquery
*/
s.executeUpdate("insert into t values (1,10,1), (2,40,1)," +
" (3,45,1), (4,46,1), (5,90,1)");
rs = s.executeQuery(
"select * from t where i in (select i/5 from t order by 1 desc)");
JDBC.assertFullResultSet(rs, new String[][]{{"1", "10", "1"}});
rs = s.executeQuery(
"select * from t where i in (select i/5 from t order by i/5 desc)");
JDBC.assertFullResultSet(rs, new String[][]{{"1", "10", "1"}});
rs = s.executeQuery(
"select * from t where i in (select i/5 from t order by j)");
JDBC.assertFullResultSet(rs, new String[][]{{"1", "10", "1"}});
/*
* Scalar subquery inside ALL subquery with correlation
*/
String[][] expected = new String[][]{
{"1", "10", "1"},
{"2", "40", "1"}};
// First without any ORDER BYs
rs = s.executeQuery(
"select * from t t_o where i <= all (" +
" select i+1 from t where i = t_o.k + (" +
" select count(*) from t) - 5)");
JDBC.assertFullResultSet(rs, expected);
// Then with ORDER BY at both subquery levels; should be the same result
rs = s.executeQuery(
"select * from t t_o where i <= all (" +
" select i+1 from t where i = t_o.k + (" +
" select count(*) from t order by 1) - 5 " +
" order by 1 desc)");
JDBC.assertFullResultSet(rs, expected);
rollback();
}
/**
* {@code SELECT} subqueries with {@code ORDER BY} and {@code OFFSET/FETCH}.
* <p/>
* This test is a variant made my modifying {@code
* testSelectSubqueriesOrderBy} with suitable {@code OFFSET/FETCH FIRST}
* clauses.
*
* @throws java.sql.SQLException
*/
public void testSelectSubqueriesOrderByAndOffsetFetch()
throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
ResultSet rs;
s.execute(
"insert into t_source values (1, 'one'), (2, 'two'), (8, 'three')");
/*
* Simple SELECT FromSubquery
*/
rs = s.executeQuery(
"select * from (select c1 from t_source order by c1 desc " +
" offset 1 row) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"2"}, {"1"}});
rs = s.executeQuery(
"select * from (select c1+1 from t_source order by c1+1 desc " +
" fetch first 2 rows only) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"9"}, {"3"}});
rs = s.executeQuery(
"select * from (select c1,c2 from t_source order by c1 desc,2 " +
" offset 2 rows fetch next 1 row only) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"1", "one"}});
// Complication: project away sort column
rs = s.executeQuery(
"select * from (select c2 from t_source order by c1 desc " +
" offset 2 rows) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"one"}});
rs = s.executeQuery(
"select * from " +
" (select c2 from t_source order by c1 desc " +
" fetch first 2 row only) s order by 1");
JDBC.assertFullResultSet(rs, new String[][]{
{"three"}, {"two"}});
/*
* Simple VALUES FromSubquery
*/
rs = s.executeQuery(
"select * from (values (1, 'one'), (2, 'two'), (8, 'three')" +
" order by 1 desc offset 1 row) s");
JDBC.assertFullResultSet(rs, new String[][]{
{"2", "two"}, {"1", "one"}});
/*
* ORDER BY in EXISTS subquery
*/
rs = s.executeQuery(
"select c1 from t_source where exists " +
" (select c1 from t_source order by c1 offset 1 row)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"2"}, {"8"}});
// OFFSET so we get an empty result set:
rs = s.executeQuery(
"select c1 from t_source where exists " +
" (select c1 from t_source order by c1 offset 3 rows)");
JDBC.assertEmpty(rs);
rs = s.executeQuery(
"select c1 from t_source where exists " +
" (select c1 from t_source order by c1 desc " +
" fetch first 1 row only)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"2"}, {"8"}});
// drop order by for once:
rs = s.executeQuery(
"select c1 from t_source where exists " +
" (select c1 from t_source offset 1 row)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"2"}, {"8"}});
rs = s.executeQuery(
"select c1 from t_source where exists " +
" (select c1 from t_source order by c2 desc " +
" offset 1 row fetch first 1 row only) " +
" order by 1 desc offset 1 row fetch first 1 row only");
JDBC.assertFullResultSet(rs, new String[][]{
{"2"}});
/*
* NOT EXISTS
*/
// We offset outside inner subquery, so NOT EXISTS should hold for all
rs = s.executeQuery(
"select c1 from t_source where not exists " +
" (select c1 from t_source order by c2 desc " +
" offset 3 rows) " +
" order by 1 desc");
JDBC.assertFullResultSet(rs, new String[][]{
{"8"}, {"2"}, {"1"}});
// should remove the hit for 1 below since we offset past it:
rs = s.executeQuery(
"select c1 from t_source ot where not exists " +
" (select c1 from t_source where ot.c1=(c1/2) order by c2 desc " +
" offset 1 row)" +
" order by 1 desc");
JDBC.assertFullResultSet(rs, new String[][]{
{"8"}, {"2"}, {"1"}});
/*
* IN subquery
*/
s.executeUpdate("insert into t values (1,10,1), (2,40,1)," +
" (3,45,1), (4,46,1), (5,90,1)");
// offset away the interesting value in the subquery:
rs = s.executeQuery(
"select * from t where i in (select i/5 from t order by 1 desc " +
" offset 1 row)");
JDBC.assertEmpty(rs);
// turn rs around, and we should get a hit:
rs = s.executeQuery(
"select * from t where i in (select i/5 from t order by 1 asc " +
" offset 1 row)");
JDBC.assertFullResultSet(rs, new String[][]{{"1", "10", "1"}});
rs = s.executeQuery(
"select * from t where i in (select i/5 from t order by i/5 desc " +
" offset 1 row)");
JDBC.assertEmpty(rs);
rs = s.executeQuery(
"select * from t where i in (select i/5 from t order by i/5 asc " +
" offset 1 row)");
JDBC.assertFullResultSet(rs, new String[][]{{"1", "10", "1"}});
rs = s.executeQuery(
"select * from t where i in (select i/5 from t order by j " +
" offset 1 row)");
JDBC.assertFullResultSet(rs, new String[][]{{"1", "10", "1"}});
rs = s.executeQuery(
"select * from t where i in (select i/5 from t order by j desc " +
" offset 1 row)");
JDBC.assertEmpty(rs);
/*
* Scalar subquery inside ALL subquery with correlation
*/
String[][] expected = new String[][]{
{"1", "10", "1"},
{"2", "40", "1"}};
// First without any ORDER BYs
rs = s.executeQuery(
"select * from t t_o where i <= all (" +
" select i+1 from t where i = t_o.k + (" +
" select count(*) from t) - 5)");
JDBC.assertFullResultSet(rs, expected);
// Should give null from subquery
rs = s.executeQuery(
"select * from t where i = (select count(*) from t order by 1 " +
" offset 1 row)");
JDBC.assertEmpty(rs);
rs = s.executeQuery(
"select * from t t_o where i <= all (" +
" select i+1 from t where i = t_o.k + cast(null as int) +" +
" - 5 " +
" order by 1 desc)");
// Notice the cast(null as int) I use above to check that the
// subquery in the next query using an offset which makes the scalar
// subquery return null gives the same result as this one.
JDBC.assertFullResultSet(rs, new String[][]{
{"1","10","1"},
{"2","40","1"},
{"3","45","1"},
{"4","46","1"},
{"5","90","1"}});
rs = s.executeQuery(
"select * from t t_o where i <= all (" +
" select i+1 from t where i = t_o.k + (" +
" select count(*) from t order by 1 offset 1 row) - 5 " +
" order by 1 desc)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1","10","1"},
{"2","40","1"},
{"3","45","1"},
{"4","46","1"},
{"5","90","1"}});
rollback();
}
/**
* Test JOIN with delimited subqueries
*
* @throws java.sql.SQLException
*/
public void testJoinsWithOffsetFetch() throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
ResultSet rs;
s.execute("insert into temp1 values 'x','a','c','b','a'");
PreparedStatement ps = prepareStatement(
"select * from " +
" (select s from temp1 order by s " +
" fetch first ? rows only) t1 join " +
" (select s from temp1 order by s offset ? row " +
" fetch first ? row only) t2 " +
" on t1.s=t2.s");
ps.setInt(1,2);
ps.setInt(2,1);
ps.setInt(3,1);
rs = ps.executeQuery();
JDBC.assertFullResultSet(rs, new String[][]{
{"a", "a"},
{"a", "a"}});
ps.setInt(1,1);
rs = ps.executeQuery();
JDBC.assertFullResultSet(rs, new String[][]{
{"a", "a"}});
rollback();
}
/**
* Test {@code ORDER BY} in a view definition
*
* @throws java.sql.SQLException
*/
public void testView() throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
ResultSet rs;
s.executeUpdate(
"create view v1 as select i from t order by j desc");
s.executeUpdate(
"create view v2 as select i from t order by i");
s.executeUpdate(
"insert into t values (1,10,1), (2,40,1)," +
" (3,45,1), (4,46,1), (5,90,1)");
rs = s.executeQuery(
"select i from v1");
JDBC.assertFullResultSet(rs, new String[][]{
{"5"},{"4"},{"3"},{"2"},{"1"}});
rs = s.executeQuery(
"select i from v2");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"},{"2"},{"3"},{"4"},{"5"}});
rollback();
}
/**
* Test {@code ORDER BY} + {@code FETCH/OFFSET} in a view definition
* <p/>
* This test is a variant made my modifying {@code testView} with suitable
* {@code OFFSET/FETCH FIRST} clauses.
*
* @throws java.sql.SQLException
*/
public void testViewFetchOffset() throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
ResultSet rs;
s.executeUpdate(
"create view v1 as select i from t order by j desc " +
" offset 2 rows fetch first 1 row only");
s.executeUpdate(
"create view v2 as select i from t order by i " +
" fetch next 2 rows only");
s.executeUpdate(
"insert into t values (1,10,1), (2,40,1)," +
" (3,45,1), (4,46,1), (5,90,1)");
rs = s.executeQuery(
"select i from v1");
JDBC.assertFullResultSet(rs, new String[][]{{"3"}});
rs = s.executeQuery(
"select i from v2");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"},{"2"}});
rollback();
}
/**
* {@code SELECT} subqueries with {@code ORDER BY} - negative tests
*
* @throws java.sql.SQLException
*/
public void testSelectSubqueriesOrderByNegative() throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
ResultSet rs;
s.execute(
"insert into t_source values (1, 'one'), (2, 'two'), (8, 'three')");
/*
* Simple SELECT FromSubquery
*/
assertStatementError(
COLUMN_NOT_FOUND, s,
"select * from (select c1 from t_source order by c3 desc) s");
assertStatementError(
COLUMN_OUT_OF_RANGE, s,
"select * from (select c1 from t_source order by 3 desc) s");
/*
* Simple VALUES FromSubquery
*/
assertStatementError(
COLUMN_OUT_OF_RANGE, s,
"select * from (values (1, 'one'), (2, 'two'), (8, 'three')" +
" order by 3 desc) s");
/*
* ORDER BY in EXISTS subquery:
*/
assertStatementError(
COLUMN_NOT_FOUND, s,
"select c1 from t_source where exists " +
" (select c1 from t_source order by c4)");
rollback();
}
/**
* {@code SELECT} subqueries with {@code ORDER BY} - check sort avoidance
*
* @throws java.sql.SQLException
*/
public void testSelectSubqueriesSortAvoidance() throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
ResultSet rs;
RuntimeStatisticsParser rtsp;
s.executeUpdate("create table ts(i int, j int)");
PreparedStatement ps = prepareStatement("insert into ts values(?,?)");
for (int i=0; i < 100; i++) {
ps.setInt(1,i);
ps.setInt(2,i*2);
ps.execute();
}
s.executeUpdate("create unique index t_i on ts(i)");
s.execute("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
// ORDER BY inside a subquery should make use of index to avoid
// sorting.
rs = s.executeQuery("select * from (select i from ts order by i)tt");
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
// Verify that we use the index scan here and no sorting is incurred
assertTrue(rtsp.usedSpecificIndexForIndexScan("TS","T_I"));
assertFalse(rtsp.whatSortingRequired());
s.execute("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(0)");
rollback();
}
/**
* Prevent pushing of where predicates into selects with fetch
* and/or offset (DERBY-5911). Similarly, for windowed selects.
*
* @throws java.sql.SQLException
*/
public void testPushAvoidance() throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
s.executeUpdate
("CREATE TABLE COFFEES (COF_NAME VARCHAR(254),PRICE INTEGER)");
s.executeUpdate
("INSERT INTO COFFEES VALUES ('Colombian', 5)");
s.executeUpdate
("INSERT INTO COFFEES VALUES ('French_Roast', 5)");
s.executeUpdate
("INSERT INTO COFFEES VALUES ('Colombian_Decaf', 20)");
ResultSet rs = s.executeQuery
("select * from " +
" (select COF_NAME, PRICE from COFFEES " +
" order by COF_NAME fetch next 2 rows only" +
" ) t " +
"where t.PRICE < 10");
JDBC.assertFullResultSet(rs, new String[][]{{"Colombian", "5"}});
rs = s.executeQuery
("select * from " +
" (select COF_NAME, PRICE from COFFEES " +
" order by COF_NAME offset 2 row" +
" ) t " +
"where t.PRICE < 10");
JDBC.assertFullResultSet(rs, new String[][]{{"French_Roast", "5"}});
rs = s.executeQuery
("select cof_name, price from " +
" (select row_number() over() as rownum, COF_NAME, PRICE from " +
" (select * from COFFEES order by COF_NAME) i" +
" ) t where rownum <= 2 and PRICE < 10");
JDBC.assertFullResultSet(rs, new String[][]{{"Colombian", "5"}});
rollback();
}
/**
* Test nesting inside set operands, cf. this production in SQL
* 2011, section 7.12:
* <pre>
* <query primary> ::=
* <simple table>
* | <left paren> <query expression body>
* [ <order by clause> ] [ <result offset clause> ]
* [ <fetch first clause> ] <right paren>
* </pre>
* The corresponding production in {@code sqlgrammar.jj} is
* {@code nonJoinQueryPrimary}.
*
* Cf. DERBY-6008.
*
* @throws java.sql.SQLException
*/
public void testNestingInsideSetOperation() throws SQLException {
setAutoCommit(false);
Statement s = createStatement();
s.executeUpdate("create table t1(i int, j int )");
s.executeUpdate("create table t2(i int, j int)");
s.executeUpdate("insert into t1 values (1,1),(4,8),(2,4)");
s.executeUpdate("insert into t2 values (10,10),(40,80),(20,40)");
ResultSet rs = s.executeQuery(
"(select i from t1 order by j desc offset 1 row) union " +
"(select i from t2 order by j desc offset 1 rows " +
"fetch next 1 row only)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"2"}, {"20"}});
// Without parentheses, expect syntax error
assertCompileError("42X01",
"select i from t1 order by j desc offset 1 row union " +
"(select i from t2 order by j desc offset 2 rows)");
// With VALUES (single) instead of SELECT:
// Single values exercise changes in RowResultSetNode
rs = s.executeQuery(
"(values 1 order by 1 fetch first 1 row only) union " +
"(select i from t2 order by j desc offset 2 rows)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"10"}});
// With VALUES (single) instead of SELECT and duplicate ordering key
rs = s.executeQuery(
"(values 1 order by 1,1 fetch first 1 row only) union " +
"(select i from t2 order by j desc offset 2 rows)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"10"}});
// With VALUES (multiple) instead of SELECT
// Multiples values exercise changes in SetOperatorNode when used in
// table value constructor context (UNION).
rs = s.executeQuery(
"(values 1,2 order by 1 desc offset 1 row " +
" fetch first 1 row only)" +
" union (select i from t2 order by j desc offset 2 rows)");
JDBC.assertFullResultSet(rs, new String[][]{
{"1"}, {"10"}});
// With VALUES (multiple) instead of SELECT plus duplicate ordering
// key
rs = s.executeQuery(
"(values 1,2 order by 1,1 offset 1 row fetch first 1 row only)" +
" union (select i from t2 order by j desc offset 2 rows)");
JDBC.assertFullResultSet(rs, new String[][]{
{"2"}, {"10"}});
// Intersect and except
s.executeUpdate(
"create table countries(name varchar(20), " +
" population int, " +
" area int)");
s.executeUpdate("insert into countries values" +
"('Norway', 5033675, 385252)," +
"('Sweden', 9540065, 449964)," +
"('Denmark', 5580413, 42894)," +
"('Iceland', 320060, 103001)," +
"('Liechtenstein', 36281, 160)");
rs = s.executeQuery(
"(select name from countries " +
" order by population desc fetch first 2 rows only)" +
" intersect " +
"(select name from countries " +
" order by area desc fetch first 2 rows only)");
JDBC.assertFullResultSet(rs, new String[][]{
{"Sweden"}});
rs = s.executeQuery(
"(values ('Norway', 5033675, 385252), " +
" ('Sweden', 9540065, 449964)," +
" ('Denmark', 5580413, 42894)," +
" ('Iceland', 320060, 103001)," +
" ('Liechtenstein', 36281, 160)" +
" order by 2 desc fetch first 3 rows only)" +
" intersect " +
"(select * from countries " +
" order by area desc fetch first 3 rows only)");
// Note: we use 3 rows here to check that both sorts work the way they
// should: at the lowest level, the "order by 2 desc", then the
// "fetch first" of only three of those rows, then on top the ascending
// sort on all columns to get the data ready for the intersect.
JDBC.assertFullResultSet(rs, new String[][]{
{"Norway", "5033675", "385252"},
{"Sweden", "9540065", "449964"}});
rs = s.executeQuery(
"(values ('Norway', 5033675, 385252)" +
" order by 2 desc fetch first 3 rows only)" +
" intersect " +
"(values ('Norway', 5033675, 385252))");
JDBC.assertFullResultSet(rs, new String[][]{
{"Norway", "5033675", "385252"}});
rs = s.executeQuery(
"(select name from countries " +
" order by population desc fetch first 2 rows only)" +
" except " +
"(select name from countries " +
" order by area desc fetch first 2 rows only)");
JDBC.assertFullResultSet(rs, new String[][]{
{"Denmark"}});
rs = s.executeQuery(
"(values ('Norway', 5033675, 385252), " +
" ('Sweden', 9540065, 449964)," +
" ('Denmark', 5580413, 42894)," +
" ('Iceland', 320060, 103001)," +
" ('Liechtenstein', 36281, 160)" +
" order by 2 desc fetch first 3 rows only)" +
" except " +
"(select * from countries " +
" order by area desc fetch first 3 rows only)");
JDBC.assertFullResultSet(rs, new String[][]{
{"Denmark", "5580413", "42894"}});
rollback();
}
/**
* Nested query expression body, with each level contributing to the set of
* ORDER BY and/or OFFSET/FETCH FIRST clauses.
*
* Cf. these productions in SQL 2011, section 7.11:
*
* <pre>
* <query expression> ::=
* [ <with clause> ] <query expression body>
* [ <order by clause> ] [ <result offset clause> ]
* [ <fetch first clause> ]
*
* <query expression body> ::=
* <query term> ...
* </pre>
*
* One of the productions of {@code <query expression body>}, is
*
* <pre>
* &lt;left paren&gt; &lt;query expression body&gt;
* [ <order by clause> ] [ <result offset clause> ]
* [ <fetch first clause> ] <right paren>
* </pre>
* so our clauses nests to arbitrary depth given enough parentheses,
* including ORDER BY and OFFSET/FETCH FIRST clauses. This nesting
* did not work correctly, cf. DERBY-6378.
*
* The corresponding productions in {@code sqlgrammar.jj} is
* {@code queryExpression} and {@code nonJoinQueryPrimary}.
*
* @throws Exception
*/
public void testDerby6378() throws Exception
{
setAutoCommit(false);
Statement stm = createStatement();
stm.executeUpdate("create table t1 (a int, b bigint)");
stm.executeUpdate("delete from t1");
stm.executeUpdate("insert into t1 values " +
"(1,-10), (2,-11), (3,-9), (4,-20), (5,-1)");
queryAndCheck(stm,
"(select * from t1 offset 1 row fetch first 1 row only)",
new String [][] {{"2","-11"}});
queryAndCheck(stm,
"(select * from t1 order by a desc fetch first 3 rows only) " +
" offset 1 row fetch first 1 row only",
new String [][] {{"4","-20"}});
queryAndCheck(stm,
"((select * from t1 order by a desc) " +
" fetch first 3 rows only)",
new String [][] {{"5","-1"}, {"4","-20"}, {"3","-9"}});
queryAndCheck(stm,
"((((select * from t1 order by a desc) " +
" fetch first 3 rows only)) " +
" order by b) " +
"fetch first 1 row only",
new String [][] {{"4","-20"},});
queryAndCheck(
stm,
"(((((values (1,-10), (2,-11), (3,-9), (4,-20), (5,-1))" +
" order by 1 desc) " +
" fetch first 3 rows only)) " +
" order by 2) " +
"fetch first 1 row only",
new String [][] {{"4","-20"},});
rollback();
stm.close();
}
private void queryAndCheck(
Statement stm,
String queryText,
String [][] expectedRows) throws SQLException {
ResultSet rs = stm.executeQuery(queryText);
JDBC.assertFullResultSet(rs, expectedRows);
}
}