From 23d9ce96cd3faa8fd053d06fcc34b6e7c499845a Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Mon, 15 Jun 2026 13:46:31 -0400 Subject: [PATCH 1/4] chore(bigquery-jdbc): add the bridge Thread wrap ahead of Executor migration --- .../jdbc/BigQueryDatabaseMetaData.java | 80 +++++++++++ .../jdbc/BigQueryDatabaseMetaDataTest.java | 125 ++++++++++++++++++ 2 files changed, 205 insertions(+) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java index 32ed62d91fd6..04721f0b2445 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java @@ -72,6 +72,7 @@ import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; @@ -5264,4 +5265,83 @@ private void loadDriverVersionProperties() { throw ex; } } + + // TODO(keshav): This is a temporary compatibility bridge to wrap raw Threads into Futures. + // This should be removed when BigQueryDatabaseMetaData is refactored to use the ExecutorService + // directly. + static Future[] wrapThread(final Thread thread) { + if (thread == null) { + return null; + } + return new Future[] { + new Future() { + private volatile boolean cancelled = false; + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (cancelled || thread.getState() == Thread.State.TERMINATED) { + return false; + } + cancelled = true; + if (mayInterruptIfRunning) { + thread.interrupt(); + } + return true; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public boolean isDone() { + return cancelled || thread.getState() == Thread.State.TERMINATED; + } + + @Override + public Object get() throws InterruptedException, CancellationException { + if (isCancelled()) { + throw new CancellationException(); + } + while (thread.getState() != Thread.State.TERMINATED) { + if (isCancelled()) { + throw new CancellationException(); + } + if (thread.getState() == Thread.State.NEW) { + Thread.sleep(50); + } else { + thread.join(50); + } + } + return null; + } + + @Override + public Object get(long timeout, TimeUnit unit) + throws InterruptedException, CancellationException, TimeoutException { + if (isCancelled()) { + throw new CancellationException(); + } + long remainingNanos = unit.toNanos(timeout); + long deadline = System.nanoTime() + remainingNanos; + while (thread.getState() != Thread.State.TERMINATED) { + if (isCancelled()) { + throw new CancellationException(); + } + if (remainingNanos <= 0) { + throw new TimeoutException(); + } + long remainingMillis = TimeUnit.NANOSECONDS.toMillis(remainingNanos); + if (remainingMillis <= 0) { + remainingMillis = 1; + } + thread.join(Math.min(remainingMillis, 50)); + remainingNanos = deadline - System.nanoTime(); + } + return null; + } + } + }; + } } diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java index 58a5a7212066..9b2b82644c35 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java @@ -44,9 +44,13 @@ import java.sql.Types; import java.util.*; import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.regex.Pattern; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -3308,4 +3312,125 @@ public void testMetadataAndResultSetMetadataTypeMappingConsistency(StandardSQLTy assertEquals( metadataTypeInfo.jdbcType, (int) resultSetType, "Type mapping mismatch for " + type); } + + @Test + public void testWrapThread_NullThread() { + assertNull(BigQueryDatabaseMetaData.wrapThread(null)); + } + + @Test + public void testWrapThread_BasicLifecycle() throws Exception { + CountDownLatch startLatch = new CountDownLatch(1); + CountDownLatch finishLatch = new CountDownLatch(1); + Thread t = + new Thread( + () -> { + try { + startLatch.countDown(); + finishLatch.await(); + } catch (InterruptedException e) { + // ignore + } + }); + + Future[] futures = BigQueryDatabaseMetaData.wrapThread(t); + assertNotNull(futures); + assertEquals(1, futures.length); + Future f = futures[0]; + + // Thread is NEW (not started yet). + assertFalse(f.isDone()); + assertFalse(f.isCancelled()); + + t.start(); + startLatch.await(); + + // Thread is running. + assertFalse(f.isDone()); + assertFalse(f.isCancelled()); + + finishLatch.countDown(); + t.join(); + + // Thread is terminated. + assertTrue(f.isDone()); + assertFalse(f.isCancelled()); + assertNull(f.get()); + } + + @Test + public void testWrapThread_CancelBeforeStart() throws Exception { + Thread t = + new Thread( + () -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // ignore + } + }); + + Future f = BigQueryDatabaseMetaData.wrapThread(t)[0]; + assertTrue(f.cancel(true)); + assertTrue(f.isCancelled()); + assertTrue(f.isDone()); + + // cancel on already cancelled should return false + assertFalse(f.cancel(true)); + + assertThrows(CancellationException.class, () -> f.get()); + assertThrows(CancellationException.class, () -> f.get(1, TimeUnit.SECONDS)); + } + + @Test + public void testWrapThread_CancelRunningWithInterrupt() throws Exception { + CountDownLatch startLatch = new CountDownLatch(1); + CountDownLatch interruptedLatch = new CountDownLatch(1); + Thread t = + new Thread( + () -> { + startLatch.countDown(); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + interruptedLatch.countDown(); + } + }); + + t.start(); + startLatch.await(); + + Future f = BigQueryDatabaseMetaData.wrapThread(t)[0]; + assertTrue(f.cancel(true)); + assertTrue(f.isCancelled()); + assertTrue(f.isDone()); + + assertTrue(interruptedLatch.await(5, TimeUnit.SECONDS)); + assertThrows(CancellationException.class, () -> f.get()); + } + + @Test + public void testWrapThread_GetTimeout() throws Exception { + CountDownLatch startLatch = new CountDownLatch(1); + Thread t = + new Thread( + () -> { + startLatch.countDown(); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + // ignore + } + }); + + t.start(); + startLatch.await(); + + Future f = BigQueryDatabaseMetaData.wrapThread(t)[0]; + assertThrows(TimeoutException.class, () -> f.get(100, TimeUnit.MILLISECONDS)); + + // Cleanup: stop the thread + t.interrupt(); + t.join(); + } } From baf073ffef8a934eb18a76f41941562593546fbb Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Mon, 15 Jun 2026 13:55:43 -0400 Subject: [PATCH 2/4] merge conflicts --- .../cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java index 04721f0b2445..351542d5605c 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java @@ -5278,7 +5278,7 @@ static Future[] wrapThread(final Thread thread) { private volatile boolean cancelled = false; @Override - public boolean cancel(boolean mayInterruptIfRunning) { + public synchronized boolean cancel(boolean mayInterruptIfRunning) { if (cancelled || thread.getState() == Thread.State.TERMINATED) { return false; } @@ -5336,7 +5336,13 @@ public Object get(long timeout, TimeUnit unit) if (remainingMillis <= 0) { remainingMillis = 1; } - thread.join(Math.min(remainingMillis, 50)); + + long delay = Math.min(remainingMillis, 50); + if (thread.getState() == Thread.State.NEW) { + Thread.sleep(delay); + } else { + thread.join(delay); + } remainingNanos = deadline - System.nanoTime(); } return null; From 1821bf070c5b32d8de418ee160eb5baecce3be30 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Mon, 15 Jun 2026 14:04:20 -0400 Subject: [PATCH 3/4] update check --- .../google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java index 351542d5605c..df27c803b17a 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java @@ -5333,7 +5333,7 @@ public Object get(long timeout, TimeUnit unit) throw new TimeoutException(); } long remainingMillis = TimeUnit.NANOSECONDS.toMillis(remainingNanos); - if (remainingMillis <= 0) { + if (remainingMillis == 0) { remainingMillis = 1; } From 553a4d39c1351d8b7aae1057765e7bddad021a30 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Wed, 17 Jun 2026 11:32:14 -0400 Subject: [PATCH 4/4] remove code duplication --- .../bigquery/jdbc/BigQueryDatabaseMetaData.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java index df27c803b17a..8ed1fd8df325 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java @@ -5301,20 +5301,11 @@ public boolean isDone() { @Override public Object get() throws InterruptedException, CancellationException { - if (isCancelled()) { - throw new CancellationException(); - } - while (thread.getState() != Thread.State.TERMINATED) { - if (isCancelled()) { - throw new CancellationException(); - } - if (thread.getState() == Thread.State.NEW) { - Thread.sleep(50); - } else { - thread.join(50); - } + try { + return get(365, TimeUnit.DAYS); + } catch (TimeoutException e) { + throw new RuntimeException(e); } - return null; } @Override