This is an automated email from the ASF dual-hosted git repository.

stankiewicz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 10d3b8d316d Fix temp table cleanup for continuous read in BigQueryIO 
(#37535)
10d3b8d316d is described below

commit 10d3b8d316d76bc19b8f19508063388dd3896728
Author: RadosÅ‚aw Stankiewicz <[email protected]>
AuthorDate: Mon Oct 19 10:00:47 2026 +0200

    Fix temp table cleanup for continuous read in BigQueryIO (#37535)
    
    * Fix temp table cleanup for continuous read in BigQueryIO
    
    * Fix missing coder for CleanupOperationMessage side outputs
    
    * Fix BQ streaming input tasks: move Javadoc, fix empty table edge case, 
and add test case.
---
 .../beam/sdk/io/gcp/bigquery/BigQueryIO.java       | 242 +++++++++++++++++++--
 .../gcp/bigquery/BigQueryStorageStreamSource.java  |  21 +-
 .../io/gcp/bigquery/CleanupTempTableDoFnTest.java  | 153 +++++++++++++
 3 files changed, 396 insertions(+), 20 deletions(-)

diff --git 
a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java
 
b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java
index 0cefea4639f..07fa1a320e9 100644
--- 
a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java
+++ 
b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java
@@ -23,6 +23,7 @@ import static 
org.apache.beam.sdk.transforms.errorhandling.BadRecordRouter.BAD_R
 import static 
org.apache.beam.sdk.transforms.errorhandling.BadRecordRouter.RECORDING_ROUTER;
 import static org.apache.beam.sdk.util.Preconditions.checkArgumentNotNull;
 import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull;
+import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.MoreObjects.firstNonNull;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkState;
 
@@ -117,9 +118,14 @@ import 
org.apache.beam.sdk.options.ValueProvider.StaticValueProvider;
 import org.apache.beam.sdk.schemas.FieldAccessDescriptor;
 import org.apache.beam.sdk.schemas.ProjectionProducer;
 import org.apache.beam.sdk.schemas.Schema;
+import org.apache.beam.sdk.state.StateSpec;
+import org.apache.beam.sdk.state.StateSpecs;
+import org.apache.beam.sdk.state.ValueState;
 import org.apache.beam.sdk.transforms.Create;
 import org.apache.beam.sdk.transforms.DoFn;
 import org.apache.beam.sdk.transforms.DoFn.MultiOutputReceiver;
+import org.apache.beam.sdk.transforms.DoFn.StateId;
+import org.apache.beam.sdk.transforms.Flatten;
 import org.apache.beam.sdk.transforms.MapElements;
 import org.apache.beam.sdk.transforms.PTransform;
 import org.apache.beam.sdk.transforms.ParDo;
@@ -141,6 +147,7 @@ import org.apache.beam.sdk.values.KV;
 import org.apache.beam.sdk.values.PBegin;
 import org.apache.beam.sdk.values.PCollection;
 import org.apache.beam.sdk.values.PCollection.IsBounded;
+import org.apache.beam.sdk.values.PCollectionList;
 import org.apache.beam.sdk.values.PCollectionTuple;
 import org.apache.beam.sdk.values.PCollectionView;
 import org.apache.beam.sdk.values.Row;
@@ -957,11 +964,18 @@ public class BigQueryIO {
     DynamicRead() {}
 
     class CreateBoundedSourceForTable
-        extends DoFn<KV<String, BigQueryDynamicReadDescriptor>, 
BigQueryStorageStreamSource<T>> {
+        extends DoFn<
+            KV<String, BigQueryDynamicReadDescriptor>, KV<String, 
BigQueryStorageStreamSource<T>>> {
+
+      private final TupleTag<KV<String, CleanupOperationMessage>> 
cleanupInfoTag;
+
+      CreateBoundedSourceForTable(TupleTag<KV<String, 
CleanupOperationMessage>> cleanupInfoTag) {
+        this.cleanupInfoTag = cleanupInfoTag;
+      }
 
       @ProcessElement
       public void processElement(
-          OutputReceiver<BigQueryStorageStreamSource<T>> receiver,
+          MultiOutputReceiver receiver,
           @Element KV<String, BigQueryDynamicReadDescriptor> kv,
           PipelineOptions options)
           throws Exception {
@@ -986,7 +1000,14 @@ public class BigQueryIO {
           // shards
           long desiredChunkSize = getDesiredChunkSize(options, output);
           List<BigQueryStorageStreamSource<T>> split = 
output.split(desiredChunkSize, options);
-          split.stream().forEach(source -> receiver.output(source));
+          split.stream()
+              .forEach(
+                  source ->
+                      receiver
+                          .get(
+                              new TupleTag<KV<String, 
BigQueryStorageStreamSource<T>>>(
+                                  "mainOutput"))
+                          .output(KV.of(kv.getKey(), source)));
         } else {
           // run query
           BigQueryStorageQuerySource<T> querySource =
@@ -1023,7 +1044,21 @@ public class BigQueryIO {
           // shards
           long desiredChunkSize = getDesiredChunkSize(options, output);
           List<BigQueryStorageStreamSource<T>> split = 
output.split(desiredChunkSize, options);
-          split.stream().forEach(source -> receiver.output(source));
+          split.stream()
+              .forEach(
+                  source ->
+                      receiver
+                          .get(
+                              new TupleTag<KV<String, 
BigQueryStorageStreamSource<T>>>(
+                                  "mainOutput"))
+                          .output(KV.of(kv.getKey(), source.withFromQuery())));
+          boolean datasetCreatedByBeam = getQueryTempDataset() == null;
+          CleanupInfo cleanupInfo =
+              new CleanupInfo(
+                  queryResultTable.getTableReference(), datasetCreatedByBeam, 
split.size());
+          receiver
+              .get(cleanupInfoTag)
+              .output(KV.of(kv.getKey(), 
CleanupOperationMessage.initialize(cleanupInfo)));
         }
       }
 
@@ -1037,6 +1072,9 @@ public class BigQueryIO {
     @Override
     public PCollection<T> expand(PCollection<BigQueryDynamicReadDescriptor> 
input) {
       TupleTag<T> rowTag = new TupleTag<>();
+      TupleTag<KV<String, BigQueryStorageStreamSource<T>>> streamTag = new 
TupleTag<>("mainOutput");
+      TupleTag<KV<String, CleanupOperationMessage>> cleanupInfoTag = new 
TupleTag<>();
+
       PCollection<KV<String, BigQueryDynamicReadDescriptor>> addJobId =
           input
               .apply(
@@ -1051,21 +1089,193 @@ public class BigQueryIO {
               .apply("Checkpoint", Redistribute.byKey());
 
       PCollectionTuple resultTuple =
-          addJobId
-              .apply("Create streams", ParDo.of(new 
CreateBoundedSourceForTable()))
+          addJobId.apply(
+              "Create streams",
+              ParDo.of(new CreateBoundedSourceForTable(cleanupInfoTag))
+                  .withOutputTags(streamTag, TupleTagList.of(cleanupInfoTag)));
+
+      PCollection<KV<String, BigQueryStorageStreamSource<T>>> streams =
+          resultTuple
+              .get(streamTag)
               .setCoder(
-                  SerializableCoder.of(new 
TypeDescriptor<BigQueryStorageStreamSource<T>>() {}))
-              .apply("Redistribute", Redistribute.arbitrarily())
-              .apply(
-                  "Read Streams with storage read api",
-                  ParDo.of(
-                          new TypedRead.ReadTableSource<T>(
-                              rowTag, getParseFn(), getBadRecordRouter()))
-                      .withOutputTags(rowTag, 
TupleTagList.of(BAD_RECORD_TAG)));
+                  KvCoder.of(
+                      StringUtf8Coder.of(),
+                      SerializableCoder.of(
+                          new TypeDescriptor<BigQueryStorageStreamSource<T>>() 
{})))
+              .apply("Redistribute", Redistribute.arbitrarily());
+
+      PCollectionTuple readResultTuple =
+          streams.apply(
+              "Read Streams with storage read api",
+              ParDo.of(
+                      new ReadDynamicStreamSource<T>(
+                          rowTag, getParseFn(), getBadRecordRouter(), 
cleanupInfoTag))
+                  .withOutputTags(rowTag, 
TupleTagList.of(BAD_RECORD_TAG).and(cleanupInfoTag)));
+
+      PCollection<KV<String, CleanupOperationMessage>> cleanupMessages1 =
+          resultTuple
+              .get(cleanupInfoTag)
+              .setCoder(
+                  KvCoder.of(
+                      StringUtf8Coder.of(), 
SerializableCoder.of(CleanupOperationMessage.class)));
+
+      PCollection<KV<String, CleanupOperationMessage>> cleanupMessages2 =
+          readResultTuple
+              .get(cleanupInfoTag)
+              .setCoder(
+                  KvCoder.of(
+                      StringUtf8Coder.of(), 
SerializableCoder.of(CleanupOperationMessage.class)));
+
+      PCollectionList.of(cleanupMessages1)
+          .and(cleanupMessages2)
+          .apply(Flatten.pCollections())
+          .apply("CleanupTempTables", ParDo.of(new 
CleanupTempTableDoFn(getBigQueryServices())));
+
       getBadRecordErrorHandler()
           .addErrorCollection(
-              
resultTuple.get(BAD_RECORD_TAG).setCoder(BadRecord.getCoder(input.getPipeline())));
-      return resultTuple.get(rowTag).setCoder(getOutputCoder());
+              readResultTuple
+                  .get(BAD_RECORD_TAG)
+                  .setCoder(BadRecord.getCoder(input.getPipeline())));
+      return readResultTuple.get(rowTag).setCoder(getOutputCoder());
+    }
+  }
+
+  static class CleanupInfo implements Serializable {
+    private final String projectId;
+    private final String datasetId;
+    private final String tableId;
+    private final boolean datasetCreatedByBeam;
+    private final int totalStreams;
+
+    public CleanupInfo(TableReference tableRef, boolean datasetCreatedByBeam, 
int totalStreams) {
+      this.projectId = tableRef.getProjectId();
+      this.datasetId = tableRef.getDatasetId();
+      this.tableId = tableRef.getTableId();
+      this.datasetCreatedByBeam = datasetCreatedByBeam;
+      this.totalStreams = totalStreams;
+    }
+
+    public TableReference getTableReference() {
+      return new TableReference()
+          .setProjectId(projectId)
+          .setDatasetId(datasetId)
+          .setTableId(tableId);
+    }
+
+    public boolean isDatasetCreatedByBeam() {
+      return datasetCreatedByBeam;
+    }
+
+    public int getTotalStreams() {
+      return totalStreams;
+    }
+  }
+
+  static class CleanupOperationMessage implements Serializable {
+    private final @Nullable CleanupInfo cleanupInfo;
+    private final boolean isStreamCompletion;
+
+    private CleanupOperationMessage(@Nullable CleanupInfo cleanupInfo, boolean 
isStreamCompletion) {
+      this.cleanupInfo = cleanupInfo;
+      this.isStreamCompletion = isStreamCompletion;
+    }
+
+    public static CleanupOperationMessage streamComplete() {
+      return new CleanupOperationMessage(null, true);
+    }
+
+    public static CleanupOperationMessage initialize(CleanupInfo cleanupInfo) {
+      return new CleanupOperationMessage(cleanupInfo, false);
+    }
+
+    public @Nullable CleanupInfo getCleanupInfo() {
+      return cleanupInfo;
+    }
+
+    public boolean isStreamCompletion() {
+      return isStreamCompletion;
+    }
+  }
+
+  static class CleanupTempTableDoFn extends DoFn<KV<String, 
CleanupOperationMessage>, Void> {
+    private final BigQueryServices bqServices;
+    private static final Logger LOG = 
LoggerFactory.getLogger(CleanupTempTableDoFn.class);
+
+    @StateId("cleanupInfo")
+    private final StateSpec<ValueState<CleanupInfo>> cleanupInfoSpec = 
StateSpecs.value();
+
+    @StateId("completedStreams")
+    private final StateSpec<ValueState<Integer>> completedStreamsSpec = 
StateSpecs.value();
+
+    CleanupTempTableDoFn(BigQueryServices bqServices) {
+      this.bqServices = bqServices;
+    }
+
+    @ProcessElement
+    public void processElement(
+        @Element KV<String, CleanupOperationMessage> element,
+        @StateId("cleanupInfo") ValueState<CleanupInfo> cleanupInfoState,
+        @StateId("completedStreams") ValueState<Integer> completedStreamsState,
+        PipelineOptions options)
+        throws Exception {
+
+      CleanupOperationMessage msg = element.getValue();
+      CleanupInfo cleanupInfo = cleanupInfoState.read();
+      int completed = firstNonNull(completedStreamsState.read(), 0);
+
+      if (msg.isStreamCompletion()) {
+        completed += 1;
+        completedStreamsState.write(completed);
+      } else {
+        
cleanupInfoState.write(Preconditions.checkNotNull(msg.getCleanupInfo()));
+        cleanupInfo = msg.getCleanupInfo();
+      }
+
+      if (cleanupInfo != null && completed == cleanupInfo.getTotalStreams()) {
+        TableReference tempTable = cleanupInfo.getTableReference();
+        try (DatasetService datasetService =
+            bqServices.getDatasetService(options.as(BigQueryOptions.class))) {
+          LOG.info("Deleting temporary table with query results {}", 
tempTable);
+          datasetService.deleteTable(tempTable);
+          if (cleanupInfo.isDatasetCreatedByBeam()) {
+            LOG.info("Deleting temporary dataset with query results {}", 
tempTable.getDatasetId());
+            datasetService.deleteDataset(tempTable.getProjectId(), 
tempTable.getDatasetId());
+          }
+        } catch (Exception e) {
+          LOG.warn("Failed to delete temporary BigQuery table {}", tempTable, 
e);
+        }
+        cleanupInfoState.clear();
+        completedStreamsState.clear();
+      }
+    }
+  }
+
+  private static class ReadDynamicStreamSource<T>
+      extends DoFn<KV<String, BigQueryStorageStreamSource<T>>, T> {
+    private final TypedRead.ReadTableSource<T> readTableSource;
+    private final TupleTag<KV<String, CleanupOperationMessage>> 
streamCompletionTag;
+
+    ReadDynamicStreamSource(
+        TupleTag<T> rowTag,
+        SerializableFunction<SchemaAndRecord, T> parseFn,
+        BadRecordRouter badRecordRouter,
+        TupleTag<KV<String, CleanupOperationMessage>> streamCompletionTag) {
+      this.readTableSource = new TypedRead.ReadTableSource<>(rowTag, parseFn, 
badRecordRouter);
+      this.streamCompletionTag = streamCompletionTag;
+    }
+
+    @ProcessElement
+    public void processElement(
+        @Element KV<String, BigQueryStorageStreamSource<T>> element,
+        MultiOutputReceiver receiver,
+        PipelineOptions options)
+        throws Exception {
+      readTableSource.processElement(element.getValue(), receiver, options);
+      if (element.getValue().getFromQuery()) {
+        receiver
+            .get(streamCompletionTag)
+            .output(KV.of(element.getKey(), 
CleanupOperationMessage.streamComplete()));
+      }
     }
   }
 
diff --git 
a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryStorageStreamSource.java
 
b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryStorageStreamSource.java
index 1bc0b2895d9..6376af7d816 100644
--- 
a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryStorageStreamSource.java
+++ 
b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryStorageStreamSource.java
@@ -77,7 +77,13 @@ class BigQueryStorageStreamSource<T> extends 
BoundedSource<T> {
         toJsonString(Preconditions.checkArgumentNotNull(tableSchema, 
"tableSchema")),
         parseFn,
         outputCoder,
-        bqServices);
+        bqServices,
+        false);
+  }
+
+  public BigQueryStorageStreamSource<T> withFromQuery() {
+    return new BigQueryStorageStreamSource<>(
+        readSession, readStream, jsonTableSchema, parseFn, outputCoder, 
bqServices, true);
   }
 
   @Override
@@ -106,13 +112,13 @@ class BigQueryStorageStreamSource<T> extends 
BoundedSource<T> {
    */
   public BigQueryStorageStreamSource<T> fromExisting(ReadStream newReadStream) 
{
     return new BigQueryStorageStreamSource<>(
-        readSession, newReadStream, jsonTableSchema, parseFn, outputCoder, 
bqServices);
+        readSession, newReadStream, jsonTableSchema, parseFn, outputCoder, 
bqServices, fromQuery);
   }
 
   public BigQueryStorageStreamSource<T> fromExisting(
       SerializableFunction<SchemaAndRecord, T> parseFn) {
     return new BigQueryStorageStreamSource<>(
-        readSession, readStream, jsonTableSchema, parseFn, outputCoder, 
bqServices);
+        readSession, readStream, jsonTableSchema, parseFn, outputCoder, 
bqServices, fromQuery);
   }
 
   private final ReadSession readSession;
@@ -121,6 +127,7 @@ class BigQueryStorageStreamSource<T> extends 
BoundedSource<T> {
   private final SerializableFunction<SchemaAndRecord, T> parseFn;
   private final Coder<T> outputCoder;
   private final BigQueryServices bqServices;
+  private final boolean fromQuery;
 
   private BigQueryStorageStreamSource(
       ReadSession readSession,
@@ -128,13 +135,19 @@ class BigQueryStorageStreamSource<T> extends 
BoundedSource<T> {
       String jsonTableSchema,
       SerializableFunction<SchemaAndRecord, T> parseFn,
       Coder<T> outputCoder,
-      BigQueryServices bqServices) {
+      BigQueryServices bqServices,
+      boolean fromQuery) {
     this.readSession = Preconditions.checkArgumentNotNull(readSession, 
"readSession");
     this.readStream = Preconditions.checkArgumentNotNull(readStream, "stream");
     this.jsonTableSchema = Preconditions.checkArgumentNotNull(jsonTableSchema, 
"jsonTableSchema");
     this.parseFn = Preconditions.checkArgumentNotNull(parseFn, "parseFn");
     this.outputCoder = Preconditions.checkArgumentNotNull(outputCoder, 
"outputCoder");
     this.bqServices = Preconditions.checkArgumentNotNull(bqServices, 
"bqServices");
+    this.fromQuery = fromQuery;
+  }
+
+  public boolean getFromQuery() {
+    return fromQuery;
   }
 
   @Override
diff --git 
a/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/CleanupTempTableDoFnTest.java
 
b/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/CleanupTempTableDoFnTest.java
new file mode 100644
index 00000000000..b1fa3dc8e57
--- /dev/null
+++ 
b/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/CleanupTempTableDoFnTest.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.beam.sdk.io.gcp.bigquery;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.api.services.bigquery.model.TableReference;
+import org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO.CleanupInfo;
+import org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO.CleanupOperationMessage;
+import org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO.CleanupTempTableDoFn;
+import org.apache.beam.sdk.io.gcp.testing.FakeBigQueryServices;
+import org.apache.beam.sdk.io.gcp.testing.FakeDatasetService;
+import org.apache.beam.sdk.testing.TestPipeline;
+import org.apache.beam.sdk.transforms.Create;
+import org.apache.beam.sdk.transforms.ParDo;
+import org.apache.beam.sdk.values.KV;
+import org.apache.beam.sdk.values.PCollection;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CleanupTempTableDoFnTest {
+
+  @Rule public transient TestPipeline p = TestPipeline.create();
+
+  @Before
+  public void setUp() {
+    FakeDatasetService.setUp();
+  }
+
+  @Test
+  public void testCleanupTempTableDoFn() throws Exception {
+    FakeDatasetService fakeDatasetService = new FakeDatasetService();
+    FakeBigQueryServices fakeBqServices =
+        new FakeBigQueryServices().withDatasetService(fakeDatasetService);
+
+    TableReference tableRef =
+        new 
TableReference().setProjectId("project").setDatasetId("dataset").setTableId("table");
+
+    fakeDatasetService.createDataset(
+        tableRef.getProjectId(), tableRef.getDatasetId(), "", "", null);
+    fakeDatasetService.createTable(
+        new 
com.google.api.services.bigquery.model.Table().setTableReference(tableRef));
+
+    assertTrue(
+        fakeDatasetService.getDataset(tableRef.getProjectId(), 
tableRef.getDatasetId()) != null);
+    assertTrue(fakeDatasetService.getTable(tableRef) != null);
+
+    CleanupInfo cleanupInfo = new CleanupInfo(tableRef, true, 2);
+
+    PCollection<KV<String, CleanupOperationMessage>> input =
+        p.apply(
+            Create.of(
+                KV.of("job1", CleanupOperationMessage.initialize(cleanupInfo)),
+                KV.of("job1", CleanupOperationMessage.streamComplete()),
+                KV.of("job1", CleanupOperationMessage.streamComplete())));
+
+    input.apply(ParDo.of(new CleanupTempTableDoFn(fakeBqServices)));
+
+    p.run().waitUntilFinish();
+
+    // The dataset is deleted, so getDataset and getTable should throw a 404 
Exception or return
+    // null
+    try {
+      fakeDatasetService.getDataset(tableRef.getProjectId(), 
tableRef.getDatasetId());
+      fail("Dataset should have been deleted");
+    } catch (Exception e) {
+      assertTrue(
+          e.getMessage().contains("Tried to get a dataset")
+              || e.getMessage().contains("Not Found"));
+    }
+
+    try {
+      TableReference deletedRef =
+          new 
TableReference().setProjectId("project").setDatasetId("dataset").setTableId("table");
+      Object table = fakeDatasetService.getTable(deletedRef);
+      assertTrue(table == null);
+    } catch (Exception e) {
+      assertTrue(
+          e.getMessage().contains("Tried to get a dataset")
+              || e.getMessage().contains("Not Found"));
+    }
+  }
+
+  @Test
+  public void testCleanupTempTableDoFnWithZeroStreams() throws Exception {
+    FakeDatasetService fakeDatasetService = new FakeDatasetService();
+    FakeBigQueryServices fakeBqServices =
+        new FakeBigQueryServices().withDatasetService(fakeDatasetService);
+
+    TableReference tableRef =
+        new 
TableReference().setProjectId("project").setDatasetId("dataset").setTableId("table");
+
+    fakeDatasetService.createDataset(
+        tableRef.getProjectId(), tableRef.getDatasetId(), "", "", null);
+    fakeDatasetService.createTable(
+        new 
com.google.api.services.bigquery.model.Table().setTableReference(tableRef));
+
+    assertTrue(
+        fakeDatasetService.getDataset(tableRef.getProjectId(), 
tableRef.getDatasetId()) != null);
+    assertTrue(fakeDatasetService.getTable(tableRef) != null);
+
+    CleanupInfo cleanupInfo = new CleanupInfo(tableRef, true, 0);
+
+    PCollection<KV<String, CleanupOperationMessage>> input =
+        p.apply(
+            "CreateInputWithZeroStreams",
+            Create.of(KV.of("job1", 
CleanupOperationMessage.initialize(cleanupInfo))));
+
+    input.apply("CleanupWithZeroStreams", ParDo.of(new 
CleanupTempTableDoFn(fakeBqServices)));
+
+    p.run().waitUntilFinish();
+
+    try {
+      fakeDatasetService.getDataset(tableRef.getProjectId(), 
tableRef.getDatasetId());
+      fail("Dataset should have been deleted");
+    } catch (Exception e) {
+      assertTrue(
+          e.getMessage().contains("Tried to get a dataset")
+              || e.getMessage().contains("Not Found"));
+    }
+
+    try {
+      TableReference deletedRef =
+          new 
TableReference().setProjectId("project").setDatasetId("dataset").setTableId("table");
+      Object table = fakeDatasetService.getTable(deletedRef);
+      assertTrue(table == null);
+    } catch (Exception e) {
+      assertTrue(
+          e.getMessage().contains("Tried to get a dataset")
+              || e.getMessage().contains("Not Found"));
+    }
+  }
+}

Reply via email to