Skip to content
This repository was archived by the owner on Mar 6, 2026. It is now read-only.

Commit 6f1ce48

Browse files
committed
feat: add Client.delete_job method to remove job metadata
Note: this only removes job metadata. Use `Client.cancel_job` to stop a running job. Also, this feature is in preview and has not rolled out to all regions yet Location is required, so always pass in location. To keep the method signature consistent with the other methods, location is not a positional argument. The location from the job object is preferred.
1 parent 6502a60 commit 6f1ce48

File tree

3 files changed

+153
-2
lines changed

3 files changed

+153
-2
lines changed

google/cloud/bigquery/client.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,6 +1453,77 @@ def delete_model(
14531453
if not not_found_ok:
14541454
raise
14551455

1456+
def delete_job(
1457+
self,
1458+
job_id,
1459+
project=None,
1460+
location=None,
1461+
retry=DEFAULT_RETRY,
1462+
timeout=None,
1463+
not_found_ok=False,
1464+
):
1465+
"""[Beta] Delete job metadata from job history.
1466+
1467+
Note: This does not stop a running job. Use
1468+
:func:`~google.cloud.bigquery.client.Client.cancel_job` instead.
1469+
1470+
Args:
1471+
job_id (Union[ \
1472+
str, \
1473+
google.cloud.bigquery.job.LoadJob, \
1474+
google.cloud.bigquery.job.CopyJob, \
1475+
google.cloud.bigquery.job.ExtractJob, \
1476+
google.cloud.bigquery.job.QueryJob \
1477+
]): Job identifier.
1478+
1479+
Keyword Arguments:
1480+
project (Optional[str]):
1481+
ID of the project which owns the job (defaults to the client's project).
1482+
location (Optional[str]):
1483+
Location where the job was run. Ignored if ``job_id`` is a job
1484+
object.
1485+
retry (Optional[google.api_core.retry.Retry]):
1486+
How to retry the RPC.
1487+
timeout (Optional[float]):
1488+
The number of seconds to wait for the underlying HTTP transport
1489+
before using ``retry``.
1490+
not_found_ok (Optional[bool]):
1491+
Defaults to ``False``. If ``True``, ignore "not found" errors
1492+
when deleting the job.
1493+
"""
1494+
extra_params = {}
1495+
1496+
project, location, job_id = _extract_job_reference(
1497+
job_id, project=project, location=location
1498+
)
1499+
1500+
if project is None:
1501+
project = self.project
1502+
1503+
if location is None:
1504+
location = self.location
1505+
1506+
# Location is always required for jobs.delete()
1507+
extra_params["location"] = location
1508+
1509+
path = "/projects/{}/jobs/{}/delete".format(project, job_id)
1510+
1511+
span_attributes = {"path": path, "job_id": job_id, "location": location}
1512+
1513+
try:
1514+
self._call_api(
1515+
retry,
1516+
span_name="BigQuery.deleteJob",
1517+
span_attributes=span_attributes,
1518+
method="DELETE",
1519+
path=path,
1520+
query_params=extra_params,
1521+
timeout=timeout,
1522+
)
1523+
except google.api_core.exceptions.NotFound:
1524+
if not not_found_ok:
1525+
raise
1526+
14561527
def delete_routine(
14571528
self, routine, retry=DEFAULT_RETRY, timeout=None, not_found_ok=False
14581529
):

tests/system/test_client.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import time
2626
import unittest
2727
import uuid
28+
from typing import Optional
2829

2930
import psutil
3031
import pytest
@@ -62,6 +63,7 @@
6263
from google.cloud import bigquery_v2
6364
from google.cloud.bigquery.dataset import Dataset
6465
from google.cloud.bigquery.dataset import DatasetReference
66+
from google.cloud.bigquery.schema import SchemaField
6567
from google.cloud.bigquery.table import Table
6668
from google.cloud._helpers import UTC
6769
from google.cloud.bigquery import dbapi, enums
@@ -123,7 +125,7 @@ def _has_rows(result):
123125

124126

125127
def _make_dataset_id(prefix):
126-
return "%s%s" % (prefix, unique_resource_id())
128+
return "python_bigquery_tests_system_%s%s" % (prefix, unique_resource_id())
127129

128130

129131
def _load_json_schema(filename="schema.json"):
@@ -142,7 +144,7 @@ class Config(object):
142144
global state.
143145
"""
144146

145-
CLIENT = None
147+
CLIENT: Optional[bigquery.Client] = None
146148
CURSOR = None
147149
DATASET = None
148150

@@ -430,6 +432,24 @@ def test_delete_dataset_delete_contents_false(self):
430432
with self.assertRaises(exceptions.BadRequest):
431433
Config.CLIENT.delete_dataset(dataset)
432434

435+
def test_delete_job(self):
436+
dataset_id = _make_dataset_id("us_east1")
437+
self.temp_dataset(dataset_id, location="us-east1")
438+
full_table_id = (
439+
f"{Config.CLIENT.project}.{dataset_id}.test_delete_job_explicit_location"
440+
)
441+
table = Table(full_table_id, schema=[SchemaField("col", "STRING")])
442+
Config.CLIENT.create_table(table)
443+
query_job: bigquery.QueryJob = Config.CLIENT.query(
444+
f"SELECT COUNT(*) FROM `{full_table_id}`", location="us-east1",
445+
)
446+
query_job.result()
447+
self.assertIsNotNone(Config.CLIENT.get_job(query_job))
448+
449+
Config.CLIENT.delete_job(query_job)
450+
with self.assertRaises(NotFound):
451+
Config.CLIENT.get_job(query_job)
452+
433453
def test_get_table_w_public_dataset(self):
434454
public = "bigquery-public-data"
435455
dataset_id = "samples"

tests/unit/test_client.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2493,6 +2493,66 @@ def test_update_table_delete_property(self):
24932493
self.assertEqual(req[1]["data"], sent)
24942494
self.assertIsNone(table3.description)
24952495

2496+
def test_delete_job_not_found(self):
2497+
creds = _make_credentials()
2498+
client = self._make_one("client-proj", creds, location="client-loc")
2499+
conn = client._connection = make_connection(
2500+
google.api_core.exceptions.NotFound("job not found"),
2501+
google.api_core.exceptions.NotFound("job not found"),
2502+
)
2503+
2504+
with self.assertRaises(google.api_core.exceptions.NotFound):
2505+
client.delete_job("my-job")
2506+
2507+
conn.api_request.reset_mock()
2508+
client.delete_job("my-job", not_found_ok=True)
2509+
2510+
conn.api_request.assert_called_once_with(
2511+
method="DELETE",
2512+
path="/projects/client-proj/jobs/my-job/delete",
2513+
query_params={"location": "client-loc"},
2514+
timeout=None,
2515+
)
2516+
2517+
def test_delete_job_with_id(self):
2518+
creds = _make_credentials()
2519+
client = self._make_one(self.PROJECT, creds)
2520+
conn = client._connection = make_connection({})
2521+
2522+
client.delete_job("my-job", project="param-proj", location="param-loc")
2523+
2524+
conn.api_request.assert_called_once_with(
2525+
method="DELETE",
2526+
path="/projects/param-proj/jobs/my-job/delete",
2527+
query_params={"location": "param-loc"},
2528+
timeout=None,
2529+
)
2530+
2531+
def test_delete_job_with_resource(self):
2532+
from google.cloud.bigquery.job import QueryJob
2533+
2534+
query_resource = {
2535+
"jobReference": {
2536+
"projectId": "job-based-proj",
2537+
"jobId": "query_job",
2538+
"location": "us-east1",
2539+
},
2540+
"configuration": {"query": {}},
2541+
}
2542+
creds = _make_credentials()
2543+
client = self._make_one(self.PROJECT, creds)
2544+
conn = client._connection = make_connection(query_resource)
2545+
job_from_resource = QueryJob.from_api_repr(query_resource, client)
2546+
2547+
client.delete_job(job_from_resource)
2548+
2549+
conn.api_request.assert_called_once_with(
2550+
method="DELETE",
2551+
path="/projects/job-based-proj/jobs/query_job/delete",
2552+
query_params={"location": "us-east1"},
2553+
timeout=None,
2554+
)
2555+
24962556
def test_delete_model(self):
24972557
from google.cloud.bigquery.model import Model
24982558

0 commit comments

Comments
 (0)