Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
import java.time.LocalTime;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
Expand Down Expand Up @@ -406,6 +410,50 @@ public void testLocation() throws SQLException {
connection2.close();
}

@Test
public void testCancelLocation() throws Exception {
String connection_uri =
"jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;PROJECTID="
+ PROJECT_ID
+ ";OAUTHTYPE=3;LOCATION=EU";

Driver driver = BigQueryDriver.getRegisteredDriver();
Connection connection = driver.connect(connection_uri, new Properties());
Statement statement = connection.createStatement();

// Query a dataset in the EU with a heavy query so that we can cancel it while it runs.
String query =
"SELECT COUNT(*) FROM `bigquery-public-data.covid19_italy_eu.data_by_province` a "
+ "CROSS JOIN `bigquery-public-data.covid19_italy_eu.data_by_province` b "
+ "CROSS JOIN `bigquery-public-data.covid19_italy_eu.data_by_province` c";

// Run the query in a separate thread so we can cancel it from the main thread
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<SQLException> future = executor.submit(() -> {
try {
statement.executeQuery(query);
return null;
} catch (SQLException e) {
return e;
}
});

// Wait a short moment to let the query submit and start running
Thread.sleep(1500);

// Cancel the query execution
statement.cancel();

// Verify that the query threw a SQLException indicating it was cancelled
SQLException exception = future.get(15, TimeUnit.SECONDS);
assertNotNull(exception, "Expected SQLException to be thrown due to cancellation");
assertTrue(exception.getMessage().contains("cancelled") || exception.getMessage().contains("Job was cancelled") || exception.getMessage().contains("Query was cancelled"),
"Unexpected exception message: " + exception.getMessage());

connection.close();
executor.shutdown();
}
Comment on lines +414 to +455

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Connection, Statement, and ExecutorService resources are not closed in an exception-safe manner. If an assertion fails or an exception is thrown (e.g., during future.get()), the connection.close() and executor.shutdown() calls will be bypassed, leading to resource leaks. Use try-with-resources for the JDBC resources and a finally block to ensure the executor is shut down.

  @Test
  public void testCancelLocation() throws Exception {
    String connection_uri =
        "jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;PROJECTID="
            + PROJECT_ID
            + ";OAUTHTYPE=3;LOCATION=EU";

    Driver driver = BigQueryDriver.getRegisteredDriver();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    try (Connection connection = driver.connect(connection_uri, new Properties());
        Statement statement = connection.createStatement()) {

      // Query a dataset in the EU with a heavy query so that we can cancel it while it runs.
      String query =
          "SELECT COUNT(*) FROM bigquery_public_data.covid19_italy_eu.data_by_province a "
              + "CROSS JOIN bigquery_public_data.covid19_italy_eu.data_by_province b "
              + "CROSS JOIN bigquery_public_data.covid19_italy_eu.data_by_province c";

      // Run the query in a separate thread so we can cancel it from the main thread
      Future<SQLException> future = executor.submit(() -> {
        try {
          statement.executeQuery(query);
          return null;
        } catch (SQLException e) {
          return e;
        }
      });

      // Wait a short moment to let the query submit and start running
      Thread.sleep(1500);

      // Cancel the query execution
      statement.cancel();

      // Verify that the query threw a SQLException indicating it was cancelled
      SQLException exception = future.get(15, TimeUnit.SECONDS);
      assertNotNull(exception, "Expected SQLException to be thrown due to cancellation");
      assertTrue(exception.getMessage().contains("cancelled") || exception.getMessage().contains("Job was cancelled") || exception.getMessage().contains("Query was cancelled"),
          "Unexpected exception message: " + exception.getMessage());
    } finally {
      executor.shutdown();
    }
  }
References
  1. When managing a collection of closeable resources, ensure they are closed in the reverse order of their creation (LIFO). The implementation must be exception-safe to prevent resource leaks, meaning all opened resources should be closed even if exceptions occur during their creation or closing.


@Test
public void testIncorrectLocation() throws SQLException {
String connection_uri =
Expand Down
Loading