diff --git a/src/main/java/net/starschema/clouddb/jdbc/BQForwardOnlyResultSet.java b/src/main/java/net/starschema/clouddb/jdbc/BQForwardOnlyResultSet.java index fe69724..2b7d602 100644 --- a/src/main/java/net/starschema/clouddb/jdbc/BQForwardOnlyResultSet.java +++ b/src/main/java/net/starschema/clouddb/jdbc/BQForwardOnlyResultSet.java @@ -61,7 +61,12 @@ import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; -import java.util.*; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -593,12 +598,13 @@ public void deleteRow() throws SQLException { */ @Override public int findColumn(String columnLabel) throws SQLException { - if (this.isClosed()) { + if (isClosed()) { throw new BQSQLException("This Resultset is Closed"); } - int columncount = this.getMetaData().getColumnCount(); - for (int i = 1; i <= columncount; i++) { - if (this.getMetaData().getCatalogName(i).equals(columnLabel)) { + final ResultSetMetaData metadata = this.getMetaData(); + int columns = metadata.getColumnCount(); + for (int i = 1; i <= columns; i++) { + if (metadata.getColumnLabel(i).equals(columnLabel)) { return i; } } diff --git a/src/main/java/net/starschema/clouddb/jdbc/BQResultSet.java b/src/main/java/net/starschema/clouddb/jdbc/BQResultSet.java index ac36169..852c1fb 100644 --- a/src/main/java/net/starschema/clouddb/jdbc/BQResultSet.java +++ b/src/main/java/net/starschema/clouddb/jdbc/BQResultSet.java @@ -112,9 +112,10 @@ public int findColumn(String columnLabel) throws SQLException { if (this.isClosed()) { throw new BQSQLException("This Resultset is Closed"); } - int columncount = this.getMetaData().getColumnCount(); - for (int i = 1; i <= columncount; i++) { - if (this.getMetaData().getCatalogName(i).equals(columnLabel)) { + final ResultSetMetaData metadata = this.getMetaData(); + int columns = metadata.getColumnCount(); + for (int i = 1; i <= columns; i++) { + if (metadata.getColumnLabel(i).equals(columnLabel)) { return i; } } diff --git a/src/main/java/net/starschema/clouddb/jdbc/BQScrollableResultSet.java b/src/main/java/net/starschema/clouddb/jdbc/BQScrollableResultSet.java index d51fef0..d917344 100644 --- a/src/main/java/net/starschema/clouddb/jdbc/BQScrollableResultSet.java +++ b/src/main/java/net/starschema/clouddb/jdbc/BQScrollableResultSet.java @@ -140,9 +140,10 @@ public int findColumn(String columnLabel) throws SQLException { if (this.isClosed()) { throw new BQSQLException("This Resultset is Closed"); } - int columncount = this.getMetaData().getColumnCount(); - for (int i = 1; i <= columncount; i++) { - if (this.getMetaData().getCatalogName(i).equals(columnLabel)) { + final ResultSetMetaData metadata = this.getMetaData(); + int columns = metadata.getColumnCount(); + for (int i = 1; i <= columns; i++) { + if (metadata.getColumnLabel(i).equals(columnLabel)) { return i; } } diff --git a/src/test/java/net/starschema/clouddb/jdbc/BQForwardOnlyResultSetFunctionTest.java b/src/test/java/net/starschema/clouddb/jdbc/BQForwardOnlyResultSetFunctionTest.java index 71626be..7c1b4a3 100644 --- a/src/test/java/net/starschema/clouddb/jdbc/BQForwardOnlyResultSetFunctionTest.java +++ b/src/test/java/net/starschema/clouddb/jdbc/BQForwardOnlyResultSetFunctionTest.java @@ -28,6 +28,7 @@ import com.google.gson.Gson; import java.io.IOException; import java.math.BigDecimal; +import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; @@ -58,7 +59,7 @@ * @author Horváth Attila * @author Gunics Balazs */ -public class BQForwardOnlyResultSetFunctionTest { +public class BQForwardOnlyResultSetFunctionTest extends CommonTestsForResultSets { private static java.sql.Connection con = null; private java.sql.ResultSet resultForTest = null; @@ -845,4 +846,10 @@ public void testStatelessQuery() throws SQLException { Assertions.assertThat(bqResultSet.getJobId()).isNull(); Assertions.assertThat(bqResultSet.getQueryId()).contains("!"); } + + @Override + protected Statement createStatementForCommonTests(final Connection connection) + throws SQLException { + return connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + } } diff --git a/src/test/java/net/starschema/clouddb/jdbc/BQScrollableResultSetFunctionTest.java b/src/test/java/net/starschema/clouddb/jdbc/BQScrollableResultSetFunctionTest.java index c7e8190..d0dfa48 100644 --- a/src/test/java/net/starschema/clouddb/jdbc/BQScrollableResultSetFunctionTest.java +++ b/src/test/java/net/starschema/clouddb/jdbc/BQScrollableResultSetFunctionTest.java @@ -22,6 +22,7 @@ import com.google.api.client.testing.http.MockHttpTransport; import com.google.api.client.testing.http.MockLowLevelHttpResponse; +import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; @@ -31,6 +32,7 @@ import org.assertj.core.api.Assertions; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +43,7 @@ * @author Horváth Attila * @author Gunics Balazs */ -public class BQScrollableResultSetFunctionTest { +public class BQScrollableResultSetFunctionTest extends CommonTestsForResultSets { private static java.sql.Connection con = null; private static java.sql.ResultSet Result = null; @@ -745,4 +747,31 @@ public void testStatelessQuery() throws SQLException { Assertions.assertThat(bqResultSet.getJobId()).isNull(); Assertions.assertThat(bqResultSet.getQueryId()).contains("!"); } + + @Override + protected Statement createStatementForCommonTests(Connection connection) throws SQLException { + return connection.createStatement( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + } + + @Override + @Test + @Ignore( + "Contradiction between BQSCrollableResultSet and BQForwardOnlyResultSet " + + "dates and times: b/317107706") + public void testGetTimeByLabel() throws SQLException {} + + @Override + @Test + @Ignore( + "Contradiction between BQSCrollableResultSet and BQForwardOnlyResultSet " + + "dates and times: b/317107706") + public void testGetDateByLabel() throws SQLException {} + + @Override + @Test + @Ignore( + "Contradiction between BQSCrollableResultSet and BQForwardOnlyResultSet " + + "dates and times: b/317107706") + public void testGetTimestampByLabel() throws SQLException {} } diff --git a/src/test/java/net/starschema/clouddb/jdbc/CommonTestsForResultSets.java b/src/test/java/net/starschema/clouddb/jdbc/CommonTestsForResultSets.java new file mode 100644 index 0000000..f1368ca --- /dev/null +++ b/src/test/java/net/starschema/clouddb/jdbc/CommonTestsForResultSets.java @@ -0,0 +1,274 @@ +package net.starschema.clouddb.jdbc; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Date; +import java.util.stream.Collectors; +import org.assertj.core.api.Assertions; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public abstract class CommonTestsForResultSets { + + private Connection connection; + + @Before + public void connectForCommonTests() throws SQLException, IOException { + connection = + ConnectionFromResources.connect("installedaccount1.properties", "&useLegacySql=false"); + } + + @After + public void closeConnectionForCommonTests() throws SQLException { + connection.close(); + } + + /** + * A {@link java.util.function.Consumer} that can throw a {@link SQLException} + * + * @param The return value's type + */ + private interface ConsumerWithSQLException { + + void accept(T arg) throws SQLException; + } + + /** + * Create a {@link Statement} that returns the appropriate result set. + * + * @param connection the connection from which to create the statement + * @return a statement used by common tests + * @throws SQLException if the {@link Connection} raises one + */ + protected abstract Statement createStatementForCommonTests(final Connection connection) + throws SQLException; + + /** + * Assert something about the single row and column returned by a query. + * + * @param query must result in a single row and column + * @param check a Consumer that can throw a {@link SQLException} - put your assertions about the + * result set in this + * @throws SQLException whenever check throws it + */ + private void assertSingleRowAndColumn( + final String query, final ConsumerWithSQLException check) throws SQLException { + try (Statement stmt = createStatementForCommonTests(connection)) { + final ResultSet result = stmt.executeQuery(query); + Assertions.assertThat(result.next()).isTrue(); + check.accept(result); + Assertions.assertThat(result.next()).isFalse(); + } + } + + @Test + public void testFindColumn() throws SQLException { + assertSingleRowAndColumn( + "SELECT 2 AS two", rs -> Assertions.assertThat(rs.findColumn("two")).isEqualTo(1)); + } + + /** + * A {@link java.util.function.Function} that can throw a {@link SQLException} + * + * @param the argument's type + * @param the returned value's type + */ + private interface FunctionWithSQLException { + R apply(T arg) throws SQLException; + } + + private void assertReaderAsString( + final String query, + final FunctionWithSQLException getter, + final String value) + throws SQLException { + assertSingleRowAndColumn( + query, + rs -> { + final Reader reader = getter.apply(rs); + final String contents = + new BufferedReader(reader).lines().collect(Collectors.joining("")); + Assertions.assertThat(contents).isEqualTo(value); + }); + } + + @Test + public void testGetAsciiStreamByLabel() throws SQLException { + assertReaderAsString( + "SELECT 'an ASCII string' AS stream", + rs -> new InputStreamReader(rs.getAsciiStream("stream"), StandardCharsets.US_ASCII), + "an ASCII string"); + } + + @Test + public void testGetBinaryStreamByLabel() throws SQLException { + assertReaderAsString( + "SELECT 'a binary string' AS stream", + rs -> new InputStreamReader(rs.getBinaryStream("stream"), StandardCharsets.US_ASCII), + "a binary string"); + } + + @Test + public void testGetBooleanByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT false AS bool", rs -> Assertions.assertThat(rs.getBoolean("bool")).isFalse()); + } + + @Test + public void testGetByteByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 98 AS byte", rs -> Assertions.assertThat(rs.getByte("byte")).isEqualTo((byte) 98)); + } + + @Test + public void testGetBytesByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 'abc' AS bytes", + rs -> Assertions.assertThat(rs.getBytes("bytes")).isEqualTo(new byte[] {'a', 'b', 'c'})); + } + + @Test + public void testGetCharacterStreamByLabel() throws SQLException { + assertReaderAsString( + "SELECT 'some characters' AS chars", + rs -> rs.getCharacterStream("chars"), + "some characters"); + } + + @Test + public void testGetDateByLabel() throws SQLException { + final Calendar calendar = Calendar.getInstance(); + calendar.clear(); + calendar.set(2023, Calendar.DECEMBER, 19); + final Date expected = calendar.getTime(); + + assertSingleRowAndColumn( + "SELECT '2023-12-19' AS date", + rs -> { + final Date value = rs.getDate("date"); + Assertions.assertThat(value).isEqualTo(expected); + }); + + assertSingleRowAndColumn( + "SELECT '2023-12-19' AS date", + rs -> { + final Date value = rs.getDate("date", calendar); + Assertions.assertThat(value).isEqualTo(expected); + }); + } + + @Test + public void testGetDoubleByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 0.1 AS double", + rs -> + Assertions.assertThat(rs.getDouble("double")) + .isEqualTo(0.1, Assertions.withPrecision(1d))); + } + + @Test + public void testGetFloatByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 0.1 AS float", + rs -> + Assertions.assertThat(rs.getDouble("float")) + .isEqualTo(0.1, Assertions.withPrecision(1d))); + } + + @Test + public void testGetIntByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 6 AS integer", rs -> Assertions.assertThat(rs.getInt("integer")).isEqualTo(6)); + } + + @Test + public void testGetLongByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 6 AS long", rs -> Assertions.assertThat(rs.getInt("long")).isEqualTo(6L)); + } + + @Test + public void testGetObjectByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 'an object' AS obj", + rs -> Assertions.assertThat(rs.getObject("obj")).isEqualTo("an object")); + } + + @Test + public void testGetShortByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 32767 AS short", + rs -> Assertions.assertThat(rs.getShort("short")).isEqualTo((short) 32767)); + } + + @Test + public void testGetSQLXMLByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 'xml' AS xml", + rs -> { + final SQLXML xml = rs.getSQLXML("xml"); + final String actual = xml.getString().trim(); + final String expected = "xml"; + Assertions.assertThat(actual).isEqualTo(expected); + }); + } + + @Test + public void testGetStringByLabel() throws SQLException { + assertSingleRowAndColumn( + "SELECT 'a string' AS string", + rs -> Assertions.assertThat(rs.getString("string")).isEqualTo("a string")); + } + + @Test + public void testGetTimeByLabel() throws SQLException { + final Time expected = Time.valueOf("01:02:03"); + assertSingleRowAndColumn( + "SELECT '01:02:03' AS time", + rs -> Assertions.assertThat(rs.getTime("time")).isEqualTo(expected)); + + final Calendar calendar = Calendar.getInstance(); + calendar.clear(); + calendar.set(2023, 12, 19); + assertSingleRowAndColumn( + "SELECT '01:02:03' AS time", + rs -> Assertions.assertThat(rs.getTime("time", calendar)).isEqualTo(expected)); + } + + @Test + public void testGetTimestampByLabel() throws SQLException { + final Timestamp expected = Timestamp.valueOf("2023-12-19 01:02:03"); + assertSingleRowAndColumn( + "SELECT '2023-12-19 01:02:03' AS timestamp", + rs -> Assertions.assertThat(rs.getTimestamp("timestamp")).isEqualTo(expected)); + + final Calendar calendar = Calendar.getInstance(); + calendar.clear(); + calendar.set(2023, 12, 19); + assertSingleRowAndColumn( + "SELECT '2023-12-19 01:02:03' AS timestamp", + rs -> Assertions.assertThat(rs.getTimestamp("timestamp", calendar)).isEqualTo(expected)); + } + + @Test + public void testGetURLByLabel() throws SQLException, MalformedURLException { + final URL expected = new URL("https://127.0.0.1/p?q=1"); + assertSingleRowAndColumn( + "SELECT 'https://127.0.0.1/p?q=1' AS url", + rs -> Assertions.assertThat(rs.getURL("url")).isEqualTo(expected)); + } +}