package net.sourceforge.mayfly.acceptance;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class ResultTest extends SqlTestCase {
public void testSelectEmpty() throws Exception {
execute("CREATE TABLE foo (a INTEGER)");
ResultSet results = query("select a from foo");
assertFalse(results.next());
results.close();
}
public void testSelectFromBadTable() throws Exception {
expectQueryFailure("select a from foo", "no table foo");
}
public void testBadColumnName() throws Exception {
execute("CREATE TABLE FOO (A INTEGER)");
expectQueryFailure("select b from foo", "no column b");
}
public void testSelect() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
ResultSet results = query("select a from foo");
assertTrue(results.next());
assertEquals(5, results.getInt("a"));
assertFalse(results.next());
results.close();
}
public void testAskResultSetForUnqueriedColumn() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
ResultSet results = query("select a from foo");
assertTrue(results.next());
try {
results.getInt("b");
fail();
} catch (SQLException e) {
assertMessage("no column b", e);
}
results.close();
}
public void testExpression() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
ResultSet results = query("select a + 4 from foo");
assertTrue(results.next());
if (dialect.canGetValueViaExpressionName()) {
assertEquals(9, results.getInt("a + 4"));
}
else {
try {
results.getInt("a + 4");
fail();
} catch (SQLException e) {
assertMessage("no column a + 4", e);
}
}
results.close();
}
public void testExpressionWithDifferentWhitespace() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
ResultSet results = query("select a + 4 from foo");
assertTrue(results.next());
if (dialect.canGetValueViaExpression()) {
assertEquals(9, results.getInt("a+4"));
}
else {
try {
results.getInt("a+4");
fail();
} catch (SQLException e) {
assertMessage("no column a+4", e);
}
}
results.close();
}
public void testAs() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
ResultSet results = query("select a + 4 as total from foo");
assertTrue(results.next());
assertEquals(9, results.getInt("total"));
results.close();
}
public void testAsIsThereButNotReferenced() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
ResultSet results = query("select a as total from foo");
assertTrue(results.next());
try {
results.getInt("a");
fail();
}
catch (SQLException e) {
assertMessage("no column a", e);
}
assertEquals(5, results.getInt(1));
results.close();
}
public void testTryToGetResultsBeforeCallingNext() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
ResultSet results = query("select a from foo");
try {
results.getInt("a");
fail();
} catch (SQLException e) {
assertMessage("no current result row", e);
}
}
public void testTryToGetResultsAfterNextReturnsFalse() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
ResultSet results = query("select a from foo");
assertTrue(results.next());
assertFalse(results.next());
try {
results.getInt("a");
fail();
} catch (SQLException e) {
assertMessage("already read last result row", e);
}
}
public void testTwoRows() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
execute("INSERT INTO foo (a) values (7)");
ResultSet results = query("select a from foo");
assertTrue(results.next());
int firstResult = results.getInt("a");
assertTrue(results.next());
int secondResult = results.getInt("a");
assertFalse(results.next());
Set expected = new HashSet(Arrays.asList(new Integer[] {new Integer(5), new Integer(7)}));
Set actual = new HashSet();
actual.add(new Integer(firstResult));
actual.add(new Integer(secondResult));
assertEquals(expected, actual);
}
public void testMultipleColumns() throws Exception {
execute("CREATE TABLE foo (A INTEGER, b INTEGER)");
execute("INSERT INTO foo (a, B) values (5, 25)");
ResultSet results = query("select A, b from foo");
assertTrue(results.next());
assertEquals(5, results.getInt("a"));
assertEquals(25, results.getInt("B"));
assertFalse(results.next());
}
public void testColumnNumbers() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
ResultSet results = query("select a from foo");
assertTrue(results.next());
assertNoColumn(results, 0);
assertEquals(5, results.getInt(1));
assertNoColumn(results, 2);
assertFalse(results.next());
results.close();
}
public void testTwoMatchingColumns() throws Exception {
execute("CREATE TABLE foo (A INTEGER)");
execute("CREATE TABLE bar (A INTEGER)");
execute("INSERT INTO foo (A) values (5)");
execute("INSERT INTO bar (A) values (7)");
ResultSet results = query(
"select foo.a, bar.a from foo inner join bar on bar.a > foo.a");
assertTrue(results.next());
if (dialect.expectMayflyBehavior()) {
try {
results.getInt("a");
fail();
} catch (SQLException e) {
assertMessage("ambiguous column a", e);
}
} else {
// Seems to be in the confusing "guess what I might mean" category.
int result = results.getInt("a");
assertTrue("expected 5 or 7 but was " + result, result == 5 || result == 7);
}
assertEquals(5, results.getInt(1));
assertEquals(7, results.getInt(2));
if (dialect.maySpecifyTableDotColumnToJdbc()) {
// I guess this would make sense. Maybe we'll get around to implementing it in Mayfly.
assertEquals(5, results.getInt("foo.a"));
} else {
try {
results.getInt("foo.a");
fail();
} catch (SQLException e) {
assertMessage("column name foo.a should not contain a period", e);
}
}
}
public void testLimitWithOffset() throws Exception {
if (!dialect.haveLimit()) {
return;
}
execute("create table foo (a integer)");
execute("insert into foo (a) values (1)");
execute("insert into foo (a) values (8)");
execute("insert into foo (a) values (2)");
execute("insert into foo (a) values (7)");
execute("insert into foo (a) values (3)");
execute("insert into foo (a) values (6)");
execute("insert into foo (a) values (5)");
execute("insert into foo (a) values (4)");
assertResultList(new String[] {"4", "5"}, query("select a from foo order by a limit 2 offset 3"));
assertResultList(new String[] {"7", "8"}, query("select a from foo order by a limit 50 offset 6"));
assertResultList(new String[] { }, query("select a from foo order by a limit 50 offset 8"));
assertResultList(new String[] { }, query("select a from foo order by a limit 50 offset 9"));
// Without an ORDER BY, just reject LIMIT (The postgres manual specifically
// warns against LIMIT without ORDER BY, for example).
String limitWithoutOrderBy = "select a from foo limit 2 offset 3";
if (!dialect.canHaveLimitWithoutOrderBy()) {
expectQueryFailure(limitWithoutOrderBy, "Must specify ORDER BY with LIMIT");
} else {
// Don't know which rows we'll get, but we should get exactly 2 of them.
ResultSet results = query(limitWithoutOrderBy);
assertTrue(results.next());
assertTrue(results.next());
assertFalse(results.next());
results.close();
}
}
public void testLimitWithInadequateOrderBy() throws Exception {
if (!dialect.haveLimit()) {
return;
}
execute("create table foo (x integer, y varchar(255))");
execute("insert into foo (x, y) values (1, 'a')");
execute("insert into foo (x, y) values (1, 'c')");
execute("insert into foo (x, y) values (1, 'd')");
execute("insert into foo (x, y) values (1, 'b')");
execute("insert into foo (x, y) values (2, 'e')");
// Perhaps we should detect this case and give an error.
// Under what circumstance? Making each ORDER BY contain one
// column which is declared UNIQUE seems like too much(?).
// Insisting that the actual
// data returned have an order which is constrained by the ORDER BY
// might be right, but depends on a test hitting that case.
// Then again, isn't there a use case where a user interface lets
// the user ORDER BY, say, last name. Do we want to insist that
// the SQL actually say something like "ORDER BY lastname, id"
// (which probably makes more sense than lettting the database
// pick a random order, but might be nit-picky to require)?
// Don't know which rows we'll get, but we should get exactly 2 of them.
ResultSet results = query("select y from foo order by x limit 2");
assertTrue(results.next());
assertTrue(results.next());
assertFalse(results.next());
results.close();
}
public void testTopNQuery() throws Exception {
// Goal here is to get N rows with the lowest values
// of x, plus all "ties" (rows with the same value of x as
// the last row).
// In this example, N == 2 so we end up getting 'a'
// 'b' as part of the "N", and 'c' because it is a tie.
execute("create table foo (x integer, y varchar(255))");
execute("insert into foo (x, y) values (1, 'a')");
execute("insert into foo (x, y) values (1, 'c')");
execute("insert into foo (x, y) values (1, 'b')");
execute("insert into foo (x, y) values (2, 'e')");
// There are other ways to write this query (one involves
// the "RANK() OVER" feature from SQL2003), but this
// looks like a pretty sane one.
String topNViaSubselectAndLimit =
"SELECT y FROM foo WHERE x <= " +
"(SELECT x FROM foo ORDER BY x ASC LIMIT 1 OFFSET 1) ";
if (dialect.haveLimit()) {
assertResultSet(new String[] { " 'a' ", " 'b' ", " 'c' " },
query(topNViaSubselectAndLimit)
);
}
else {
expectQueryFailure(topNViaSubselectAndLimit, "no LIMIT");
}
}
public void testLimitNoOffset() throws Exception {
execute("create table foo (a integer)");
execute("insert into foo (a) values (2)");
execute("insert into foo (a) values (1)");
String sql = "select a from foo order by a limit 1";
if (!dialect.haveLimit()) {
expectQueryFailure(sql, "expected end of file but got limit");
return;
}
assertResultList(new String[] {"1"}, query(sql));
assertResultList(new String[] {"1", "2"}, query("select a from foo order by a limit 2"));
assertResultList(new String[] {"1", "2"}, query("select a from foo order by a limit 3"));
}
public void testLimitOffsetAndParameters() throws Exception {
if (!dialect.haveLimit()) {
return;
}
execute("create table foo(x integer)");
execute("insert into foo(x) values(5)");
execute("insert into foo(x) values(7)");
execute("insert into foo(x) values(9)");
execute("insert into foo(x) values(4)");
PreparedStatement query =
connection.prepareStatement(
"select x from foo order by x limit ? offset ?");
query.setInt(1, 2);
query.setInt(2, 1);
assertResultSet(
new String[] { " 5 ", " 7 " },
query.executeQuery());
}
public void testSelectAll() throws Exception {
execute("create table foo (x integer, y integer)");
execute("insert into foo(x, y) values (3, 7)");
ResultSet results = query("select * from foo");
assertTrue(results.next());
assertEquals(3, results.getInt(1));
assertEquals(7, results.getInt(2));
assertNoColumn(results, 3);
assertFalse(results.next());
}
public void testSelectAllWithJoin() throws Exception {
execute("create table foo (x integer, y integer)");
execute("create table bar (x integer, z integer)");
execute("insert into foo(x, y) values (3, 7)");
execute("insert into foo(x, y) values (5, 9)");
execute("insert into bar(x, z) values (3, 80)");
execute("insert into bar(x, z) values (4, 70)");
ResultSet results = query("select * from foo inner join bar on foo.x = bar.x");
assertTrue(results.next());
assertEquals(3, results.getInt(1));
assertEquals(7, results.getInt(2));
assertEquals(3, results.getInt(3));
assertEquals(80, results.getInt(4));
assertNoColumn(results, 5);
assertFalse(results.next());
}
public void testSelectAllFromTable() throws Exception {
execute("create table foo (x integer, y integer)");
execute("create table bar (x integer, z integer)");
execute("insert into foo(x, y) values (3, 7)");
execute("insert into foo(x, y) values (5, 9)");
execute("insert into bar(x, z) values (3, 80)");
execute("insert into bar(x, z) values (4, 70)");
ResultSet results = query("select bar.* from foo inner join bar on foo.x = bar.x");
assertTrue(results.next());
assertEquals(3, results.getInt(1));
assertEquals(80, results.getInt(2));
assertNoColumn(results, 3);
assertFalse(results.next());
}
private void assertNoColumn(ResultSet results, int columnIndex) {
try {
results.getInt(columnIndex);
fail();
} catch (SQLException e) {
assertMessage("no column " + columnIndex, e);
}
}
}