Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BigQueryResultImpl.getDouble throws ClassCastException if read API is used #2905

Closed
obada-ab opened this issue Sep 28, 2023 · 1 comment · Fixed by #2919
Closed

BigQueryResultImpl.getDouble throws ClassCastException if read API is used #2905

obada-ab opened this issue Sep 28, 2023 · 1 comment · Fixed by #2919
Assignees
Labels
api: bigquery Issues related to the googleapis/java-bigquery API. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@obada-ab
Copy link
Member

obada-ab commented Sep 28, 2023

Line causing the issue:

return curVal == null ? 0.0d : ((BigDecimal) curVal).doubleValue();

Steps to reproduce

Call getDouble in a read API path result set.

Can be reproduced by adding the following test to ITBigQueryTest (copied from testReadAPIIterationAndOrderAsync with minor changes):

  @Test
  public void testReadAPIGetDouble()
      throws SQLException, ExecutionException,
      InterruptedException {
    String query =
        "SELECT date, county, state_name, confirmed_cases, deaths FROM "
            + TABLE_ID_LARGE.getTable()
            + " where date is not null and county is not null and state_name is not null order by confirmed_cases asc limit 300000";

    ConnectionSettings connectionSettings =
        ConnectionSettings.newBuilder()
            .setDefaultDataset(DatasetId.of(DATASET))
            .setPriority(
                QueryJobConfiguration.Priority
                    .INTERACTIVE)
            .build();
    Connection connection = bigquery.createConnection(connectionSettings);

    ListenableFuture<ExecuteSelectResponse> executeSelectFut = connection.executeSelectAsync(query);
    ExecuteSelectResponse exSelRes = executeSelectFut.get();
    BigQueryResult bigQueryResult = exSelRes.getResultSet();
    ResultSet rs = bigQueryResult.getResultSet();
    int cnt = 0;
    int lasConfirmedCases = Integer.MIN_VALUE;
    while (rs.next()) {
      assertTrue(rs.getDouble(3) >= 0); // throws ClassCastException
      ++cnt;
    }
    assertEquals(300000, cnt);
    connection.close();
  }

Stack trace

java.lang.ClassCastException: class java.lang.Long cannot be cast to class java.math.BigDecimal (java.lang.Long and java.math.BigDecimal are in module java.base of loader 'bootstrap')

	at com.google.cloud.bigquery.BigQueryResultImpl$BigQueryResultSet.getDouble(BigQueryResultImpl.java:340)
	at com.google.cloud.bigquery.BigQueryResultImpl$BigQueryResultSet.getDouble(BigQueryResultImpl.java:355)
	at com.google.cloud.bigquery.it.ITBigQueryTest.testReadAPIGetDouble(ITBigQueryTest.java:3296)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:299)
	at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:293)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.lang.Thread.run(Thread.java:833)

Workaround

Adding setUseReadAPI(false) to force-avoid read API:

ConnectionSettings connectionSettings =
        ConnectionSettings.newBuilder()
            .setDefaultDataset(DatasetId.of(DATASET))
            .setPriority(
                QueryJobConfiguration.Priority
                    .INTERACTIVE)
            .setUseReadAPI(false)
            .build();

References

Internal b/300130086

@product-auto-label product-auto-label bot added the api: bigquery Issues related to the googleapis/java-bigquery API. label Sep 28, 2023
@obada-ab obada-ab added type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. priority: p2 Moderately-important priority. Fix may not be included in next release. labels Sep 28, 2023
@obada-ab obada-ab self-assigned this Sep 28, 2023
@obada-ab
Copy link
Member Author

obada-ab commented Sep 28, 2023

Another (slightly less clean) workaround is using getObject instead of getDouble, this would avoid the potential performance impact of setUseReadAPI(false) .

Since the returned object depends on which query path is taken, there are two cases to handle: parsing from a String object (fast query path), or casting a double object (read API):

  Object value = rs.getObject(3);
  double doubleValue;
  if (value instanceof String) {
    doubleValue = Double.parseDouble((String) value);
  } else {
    doubleValue = (double) value;
  }

(this assumes that the value returned by the query is a floating point value)

gcf-merge-on-green bot pushed a commit that referenced this issue Oct 3, 2023
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [X] Make sure to open an issue as a [bug/issue](https://togithub.com/googleapis/java-bigquery/issues/new/choose) before writing your code!  That way we can discuss the change, evaluate designs, and agree on the general idea
- [X] Ensure the tests and linter pass
- [X] Code coverage does not decrease (if any source code was changed)
- [X] Appropriate docs were updated (if necessary)

Fixes #2905 ☕️
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: bigquery Issues related to the googleapis/java-bigquery API. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant