Skip to content

Commit e265db6

Browse files
authored
fix: use an allowlist instead of denylist to determine when query_and_wait uses jobs.query API (#1869)
1 parent b0e95a0 commit e265db6

File tree

2 files changed

+49
-19
lines changed

2 files changed

+49
-19
lines changed

google/cloud/bigquery/_job_helpers.py

+36-17
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,13 @@ def query_and_wait(
400400
:class:`~google.cloud.bigquery.job.QueryJobConfig`
401401
class.
402402
"""
403+
request_body = _to_query_request(
404+
query=query, job_config=job_config, location=location, timeout=api_timeout
405+
)
406+
403407
# Some API parameters aren't supported by the jobs.query API. In these
404408
# cases, fallback to a jobs.insert call.
405-
if not _supported_by_jobs_query(job_config):
409+
if not _supported_by_jobs_query(request_body):
406410
return _wait_or_cancel(
407411
query_jobs_insert(
408412
client=client,
@@ -424,9 +428,6 @@ def query_and_wait(
424428
)
425429

426430
path = _to_query_path(project)
427-
request_body = _to_query_request(
428-
query=query, job_config=job_config, location=location, timeout=api_timeout
429-
)
430431

431432
if page_size is not None and max_results is not None:
432433
request_body["maxResults"] = min(page_size, max_results)
@@ -506,20 +507,38 @@ def do_query():
506507
return do_query()
507508

508509

509-
def _supported_by_jobs_query(job_config: Optional[job.QueryJobConfig]) -> bool:
510+
def _supported_by_jobs_query(request_body: Dict[str, Any]) -> bool:
510511
"""True if jobs.query can be used. False if jobs.insert is needed."""
511-
if job_config is None:
512-
return True
513-
514-
return (
515-
# These features aren't supported by jobs.query.
516-
job_config.clustering_fields is None
517-
and job_config.destination is None
518-
and job_config.destination_encryption_configuration is None
519-
and job_config.range_partitioning is None
520-
and job_config.table_definitions is None
521-
and job_config.time_partitioning is None
522-
)
512+
request_keys = frozenset(request_body.keys())
513+
514+
# Per issue: https://github.com/googleapis/python-bigquery/issues/1867
515+
# use an allowlist here instead of a denylist because the backend API allows
516+
# unsupported parameters without any warning or failure. Instead, keep this
517+
# set in sync with those in QueryRequest:
518+
# https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#QueryRequest
519+
keys_allowlist = {
520+
"kind",
521+
"query",
522+
"maxResults",
523+
"defaultDataset",
524+
"timeoutMs",
525+
"dryRun",
526+
"preserveNulls",
527+
"useQueryCache",
528+
"useLegacySql",
529+
"parameterMode",
530+
"queryParameters",
531+
"location",
532+
"formatOptions",
533+
"connectionProperties",
534+
"labels",
535+
"maximumBytesBilled",
536+
"requestId",
537+
"createSession",
538+
}
539+
540+
unsupported_keys = request_keys - keys_allowlist
541+
return len(unsupported_keys) == 0
523542

524543

525544
def _wait_or_cancel(

tests/unit/test__job_helpers.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import pytest
2323

2424
from google.cloud.bigquery.client import Client
25+
from google.cloud.bigquery import enums
2526
from google.cloud.bigquery import _job_helpers
2627
from google.cloud.bigquery.job import copy_ as job_copy
2728
from google.cloud.bigquery.job import extract as job_extract
@@ -1141,12 +1142,22 @@ def test_make_job_id_w_job_id_overrides_prefix():
11411142
False,
11421143
id="destination_encryption_configuration",
11431144
),
1145+
# priority="BATCH" is not supported. See:
1146+
# https://github.com/googleapis/python-bigquery/issues/1867
1147+
pytest.param(
1148+
job_query.QueryJobConfig(
1149+
priority=enums.QueryPriority.BATCH,
1150+
),
1151+
False,
1152+
id="priority=BATCH",
1153+
),
11441154
),
11451155
)
1146-
def test_supported_by_jobs_query(
1156+
def test_supported_by_jobs_query_from_queryjobconfig(
11471157
job_config: Optional[job_query.QueryJobConfig], expected: bool
11481158
):
1149-
assert _job_helpers._supported_by_jobs_query(job_config) == expected
1159+
request_body = _job_helpers._to_query_request(job_config, query="SELECT 1")
1160+
assert _job_helpers._supported_by_jobs_query(request_body) == expected
11501161

11511162

11521163
def test_wait_or_cancel_no_exception():

0 commit comments

Comments
 (0)