Skip to content

Commit 2695aad

Browse files
authored
feat(bigtable): add view_parameters support to execute_query (googleapis#17382)
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/google-cloud-python/issues) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [ ] Ensure the tests and linter pass - [ ] Code coverage does not decrease (if any source code was changed) - [ ] Appropriate docs were updated (if necessary) Fixes #<issue_number_goes_here> 🦕
1 parent 35af616 commit 2695aad

5 files changed

Lines changed: 107 additions & 0 deletions

File tree

packages/google-cloud-bigtable/google/cloud/bigtable/data/_async/client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
)
7575
from google.cloud.bigtable.data.execute_query._parameters_formatting import (
7676
_format_execute_query_params,
77+
_format_execute_query_view_params,
7778
_to_param_types,
7879
)
7980
from google.cloud.bigtable.data.execute_query.metadata import (
@@ -717,6 +718,7 @@ async def execute_query(
717718
*,
718719
parameters: dict[str, ExecuteQueryValueType] | None = None,
719720
parameter_types: dict[str, SqlType.Type] | None = None,
721+
view_parameters: dict[str, str] | None = None,
720722
app_profile_id: str | None = None,
721723
operation_timeout: float = 600,
722724
attempt_timeout: float | None = 20,
@@ -758,6 +760,8 @@ async def execute_query(
758760
Required to contain entries only for parameters whose type cannot be
759761
detected automatically (i.e. the value can be None, an empty list or
760762
an empty dict).
763+
view_parameters: Dictionary with values for all view parameters. Currently only
764+
string values are supported.
761765
app_profile_id: The app profile to associate with requests.
762766
https://cloud.google.com/bigtable/docs/app-profiles
763767
operation_timeout: the time budget for the entire executeQuery operation, in seconds.
@@ -883,12 +887,14 @@ async def execute_query(
883887
retryable_excs = [_get_error_type(e) for e in retryable_errors]
884888

885889
pb_params = _format_execute_query_params(parameters, parameter_types)
890+
pb_view_params = _format_execute_query_view_params(view_parameters)
886891

887892
request_body = {
888893
"instance_name": instance_name,
889894
"app_profile_id": app_profile_id,
890895
"prepared_query": prepare_result.prepared_query,
891896
"params": pb_params,
897+
"view_parameters": pb_view_params,
892898
}
893899
operation_timeout, attempt_timeout = _align_timeouts(
894900
operation_timeout, attempt_timeout

packages/google-cloud-bigtable/google/cloud/bigtable/data/_sync_autogen/client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
)
7676
from google.cloud.bigtable.data.execute_query._parameters_formatting import (
7777
_format_execute_query_params,
78+
_format_execute_query_view_params,
7879
_to_param_types,
7980
)
8081
from google.cloud.bigtable.data.execute_query.metadata import (
@@ -532,6 +533,7 @@ def execute_query(
532533
*,
533534
parameters: dict[str, ExecuteQueryValueType] | None = None,
534535
parameter_types: dict[str, SqlType.Type] | None = None,
536+
view_parameters: dict[str, str] | None = None,
535537
app_profile_id: str | None = None,
536538
operation_timeout: float = 600,
537539
attempt_timeout: float | None = 20,
@@ -572,6 +574,8 @@ def execute_query(
572574
Required to contain entries only for parameters whose type cannot be
573575
detected automatically (i.e. the value can be None, an empty list or
574576
an empty dict).
577+
view_parameters: Dictionary with values for all view parameters. Currently only
578+
string values are supported.
575579
app_profile_id: The app profile to associate with requests.
576580
https://cloud.google.com/bigtable/docs/app-profiles
577581
operation_timeout: the time budget for the entire executeQuery operation, in seconds.
@@ -692,11 +696,13 @@ def execute_query(
692696
prepare_metadata = _pb_metadata_to_metadata_types(prepare_result.metadata)
693697
retryable_excs = [_get_error_type(e) for e in retryable_errors]
694698
pb_params = _format_execute_query_params(parameters, parameter_types)
699+
pb_view_params = _format_execute_query_view_params(view_parameters)
695700
request_body = {
696701
"instance_name": instance_name,
697702
"app_profile_id": app_profile_id,
698703
"prepared_query": prepare_result.prepared_query,
699704
"params": pb_params,
705+
"view_parameters": pb_view_params,
700706
}
701707
operation_timeout, attempt_timeout = _align_timeouts(
702708
operation_timeout, attempt_timeout

packages/google-cloud-bigtable/google/cloud/bigtable/data/execute_query/_parameters_formatting.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,27 @@
2323
from google.cloud.bigtable_v2.types.data import Value
2424

2525

26+
def _format_execute_query_view_params(
27+
view_parameters: Optional[Dict[str, str]],
28+
) -> Dict[str, Value]:
29+
"""
30+
Takes a dictionary of view_param_name -> view_param_value (string) and formats
31+
them into a dictionary of string-typed Value objects.
32+
"""
33+
if not view_parameters:
34+
return {}
35+
36+
result_values = {}
37+
for key, value in view_parameters.items():
38+
if not isinstance(value, str):
39+
raise TypeError(
40+
f"View parameter {key} must be a string, got {type(value).__name__}"
41+
)
42+
result_values[key] = Value(string_value=value)
43+
44+
return result_values
45+
46+
2647
def _format_execute_query_params(
2748
params: Optional[Dict[str, ExecuteQueryValueType]],
2849
parameter_types: Optional[Dict[str, SqlType.Type]],

packages/google-cloud-bigtable/tests/unit/data/_async/test_client.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3507,6 +3507,44 @@ async def test_execute_query_with_params(
35073507
assert execute_query_mock.call_count == 1
35083508
assert prepare_mock.call_count == 1
35093509

3510+
@CrossSync.pytest
3511+
async def test_execute_query_with_view_parameters(
3512+
self, client, execute_query_mock, prepare_mock
3513+
):
3514+
values = [
3515+
*chunked_responses(2, str_val("test2"), int_val(9), token=b"r2"),
3516+
]
3517+
execute_query_mock.return_value = self._make_gapic_stream(values)
3518+
query_str = f"SELECT a, b FROM {self.TABLE_NAME} WHERE user_id = VIEW_PARAMETERS('user_id')"
3519+
result = await client.execute_query(
3520+
query_str,
3521+
self.INSTANCE_NAME,
3522+
view_parameters={"user_id": "alice"},
3523+
)
3524+
results = [r async for r in result]
3525+
assert len(results) == 1
3526+
assert results[0]["a"] == "test2"
3527+
assert results[0]["b"] == 9
3528+
assert execute_query_mock.call_count == 1
3529+
assert prepare_mock.call_count == 1
3530+
assert prepare_mock.call_args[1]["request"]["query"] == query_str
3531+
3532+
request = execute_query_mock.call_args[0][0]
3533+
assert "user_id" in request.view_parameters
3534+
assert request.view_parameters["user_id"].string_value == "alice"
3535+
3536+
@CrossSync.pytest
3537+
async def test_execute_query_with_view_parameters_invalid_type(
3538+
self, client, execute_query_mock, prepare_mock
3539+
):
3540+
with pytest.raises(TypeError) as e:
3541+
await client.execute_query(
3542+
f"SELECT a, b FROM {self.TABLE_NAME}",
3543+
self.INSTANCE_NAME,
3544+
view_parameters={"user_id": 123},
3545+
)
3546+
assert "View parameter user_id must be a string, got int" in str(e.value)
3547+
35103548
@CrossSync.pytest
35113549
async def test_execute_query_error_before_metadata(
35123550
self, client, execute_query_mock, prepare_mock

packages/google-cloud-bigtable/tests/unit/data/_sync_autogen/test_client.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2973,6 +2973,42 @@ def test_execute_query_with_params(self, client, execute_query_mock, prepare_moc
29732973
assert execute_query_mock.call_count == 1
29742974
assert prepare_mock.call_count == 1
29752975

2976+
def test_execute_query_with_view_parameters(
2977+
self, client, execute_query_mock, prepare_mock
2978+
):
2979+
values = [
2980+
*chunked_responses(2, str_val("test2"), int_val(9), token=b"r2"),
2981+
]
2982+
execute_query_mock.return_value = self._make_gapic_stream(values)
2983+
query_str = f"SELECT a, b FROM {self.TABLE_NAME} WHERE user_id = VIEW_PARAMETERS('user_id')"
2984+
result = client.execute_query(
2985+
query_str,
2986+
self.INSTANCE_NAME,
2987+
view_parameters={"user_id": "alice"},
2988+
)
2989+
results = [r for r in result]
2990+
assert len(results) == 1
2991+
assert results[0]["a"] == "test2"
2992+
assert results[0]["b"] == 9
2993+
assert execute_query_mock.call_count == 1
2994+
assert prepare_mock.call_count == 1
2995+
assert prepare_mock.call_args[1]["request"]["query"] == query_str
2996+
2997+
request = execute_query_mock.call_args[0][0]
2998+
assert "user_id" in request.view_parameters
2999+
assert request.view_parameters["user_id"].string_value == "alice"
3000+
3001+
def test_execute_query_with_view_parameters_invalid_type(
3002+
self, client, execute_query_mock, prepare_mock
3003+
):
3004+
with pytest.raises(TypeError) as e:
3005+
client.execute_query(
3006+
f"SELECT a, b FROM {self.TABLE_NAME}",
3007+
self.INSTANCE_NAME,
3008+
view_parameters={"user_id": 123},
3009+
)
3010+
assert "View parameter user_id must be a string, got int" in str(e.value)
3011+
29763012
def test_execute_query_error_before_metadata(
29773013
self, client, execute_query_mock, prepare_mock
29783014
):

0 commit comments

Comments
 (0)