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

Commit 9132e80

Browse files
committed
Address review comments
1 parent c85c31e commit 9132e80

5 files changed

Lines changed: 117 additions & 29 deletions

File tree

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

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
Set,
2424
Sequence,
2525
TYPE_CHECKING,
26+
Union,
2627
)
2728

2829
import abc
@@ -58,6 +59,8 @@
5859
from google.api_core.exceptions import DeadlineExceeded
5960
from google.api_core.exceptions import ServiceUnavailable
6061
from google.api_core.exceptions import Aborted
62+
from google.protobuf.message import Message
63+
from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper
6164

6265
import google.auth.credentials
6366
import google.auth._default
@@ -657,7 +660,7 @@ async def execute_query(
657660
DeadlineExceeded,
658661
ServiceUnavailable,
659662
),
660-
column_info: dict[str, Any] | None = None,
663+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
661664
) -> "ExecuteQueryIteratorAsync":
662665
"""
663666
Executes an SQL query on an instance.
@@ -706,13 +709,54 @@ async def execute_query(
706709
If None, defaults to prepare_operation_timeout.
707710
prepare_retryable_errors: a list of errors that will be retried if encountered during prepareQuery.
708711
Defaults to 4 (DeadlineExceeded) and 14 (ServiceUnavailable)
709-
column_info: Dictionary with mappings between column names and additional column information.
710-
An object where column names as keys and custom objects as corresponding
711-
values for deserialization. It's specifically useful for data types like
712-
protobuf where deserialization logic is on user-specific code. When provided,
713-
the custom object enables deserialization of backend-received column data.
714-
If not provided, data remains serialized as bytes for Proto Messages and
715-
integer for Proto Enums.
712+
column_info:
713+
(Optional) A dictionary mapping column names to Protobuf message classes or EnumTypeWrapper objects.
714+
This dictionary provides the necessary type information for deserializing PROTO and
715+
ENUM column values from the query results. When an entry is provided
716+
for a PROTO or ENUM column, the client library will attempt to deserialize the raw data.
717+
718+
- For PROTO columns: The value in the dictionary should be the
719+
Protobuf Message class (e.g., `my_pb2.MyMessage`).
720+
- For ENUM columns: The value should be the Protobuf EnumTypeWrapper
721+
object (e.g., `my_pb2.MyEnum`).
722+
723+
Example:
724+
import my_pb2
725+
726+
column_info = {
727+
"my_proto_column": my_pb2.MyMessage,
728+
"my_enum_column": my_pb2.MyEnum
729+
}
730+
731+
If `column_info` is not provided, or if a specific column name is not found
732+
in the dictionary, or if deserialization fails:
733+
- PROTO columns will be returned as raw bytes.
734+
- ENUM columns will be returned as integers.
735+
736+
Note for Nested PROTO or ENUM Fields:
737+
To specify types for PROTO or ENUM fields within STRUCTs or MAPs, use a dot-separated
738+
path from the top-level column name.
739+
- For STRUCTs: `struct_column_name.field_name`
740+
- For MAPs: `map_column_name.key` or `map_column_name.value` to specify types
741+
for the map keys or values, respectively.
742+
743+
Examples:
744+
import my_pb2
745+
746+
column_info = {
747+
# Top-level column
748+
"my_proto_column": my_pb2.MyMessage,
749+
"my_enum_column": my_pb2.MyEnum,
750+
751+
# Nested field in a STRUCT column named 'my_struct'
752+
"my_struct.nested_proto_field": my_pb2.OtherMessage,
753+
"my_struct.nested_enum_field": my_pb2.AnotherEnum,
754+
755+
# Nested field in a MAP column named 'my_map'
756+
"my_map.key": my_pb2.MapKeyEnum, # If map keys were enums
757+
"my_map.value": my_pb2.MapValueMessage,
758+
}
759+
716760
Returns:
717761
ExecuteQueryIteratorAsync: an asynchronous iterator that yields rows returned by the query
718762
Raises:

google/cloud/bigtable/data/execute_query/_async/execute_query_iterator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
Sequence,
2222
Tuple,
2323
TYPE_CHECKING,
24+
Union,
2425
)
2526
from google.api_core import retry as retries
27+
from google.protobuf.message import Message
28+
from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper
2629

2730
from google.cloud.bigtable.data.execute_query._byte_cursor import _ByteCursor
2831
from google.cloud.bigtable.data._helpers import (
@@ -87,7 +90,7 @@ def __init__(
8790
operation_timeout: float,
8891
req_metadata: Sequence[Tuple[str, str]] = (),
8992
retryable_excs: Sequence[type[Exception]] = (),
90-
column_info: Dict[str, Any] | None = None,
93+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
9194
) -> None:
9295
"""
9396
Collects responses from ExecuteQuery requests and parses them into QueryResultRows.

google/cloud/bigtable/data/execute_query/_query_result_parsing_utils.py

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
# limitations under the License.
1414
from __future__ import annotations
1515

16-
from typing import Any, Callable, Dict, Type
16+
from typing import Any, Callable, Dict, Type, Union, Optional
17+
from typing_extensions import TypeAlias
1718

1819
from google.protobuf.message import Message
1920
from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper
@@ -43,7 +44,7 @@ def _parse_array_type(
4344
value: PBValue,
4445
metadata_type: SqlType.Array,
4546
column_name: str | None,
46-
column_info: dict[str, Any] | None = None,
47+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
4748
) -> Any:
4849
"""
4950
used for parsing an array represented as a protobuf to a python list.
@@ -62,7 +63,7 @@ def _parse_map_type(
6263
value: PBValue,
6364
metadata_type: SqlType.Map,
6465
column_name: str | None,
65-
column_info: dict[str, Any] | None = None,
66+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
6667
) -> Any:
6768
"""
6869
used for parsing a map represented as a protobuf to a python dict.
@@ -103,7 +104,7 @@ def _parse_struct_type(
103104
value: PBValue,
104105
metadata_type: SqlType.Struct,
105106
column_name: str | None,
106-
column_info: dict[str, Any] | None = None,
107+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
107108
) -> Struct:
108109
"""
109110
used for parsing a struct represented as a protobuf to a
@@ -137,7 +138,7 @@ def _parse_timestamp_type(
137138
value: PBValue,
138139
metadata_type: SqlType.Timestamp,
139140
column_name: str | None,
140-
column_info: dict[str, Any] | None = None,
141+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
141142
) -> DatetimeWithNanoseconds:
142143
"""
143144
used for parsing a timestamp represented as a protobuf to DatetimeWithNanoseconds
@@ -149,10 +150,26 @@ def _parse_proto_type(
149150
value: PBValue,
150151
metadata_type: SqlType.Proto,
151152
column_name: str | None,
152-
column_info: dict[str, Any] | None = None,
153+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
153154
) -> Message | bytes:
154155
"""
155-
Parses a serialized protobuf message into a Message object.
156+
Parses a serialized protobuf message into a Message object using type information
157+
provided in column_info.
158+
159+
Args:
160+
value: The value to parse, expected to have a bytes_value attribute.
161+
metadata_type: The expected SQL type (Proto).
162+
column_name: The name of the column.
163+
column_info: (Optional) A dictionary mapping column names to their
164+
corresponding Protobuf Message classes. This information is used
165+
to deserialize the raw bytes.
166+
167+
Returns:
168+
A deserialized Protobuf Message object if parsing is successful.
169+
If parsing fails for any reason, or if the required type information
170+
is not found in column_info, the function returns the original
171+
serialized data as bytes (value.bytes_value). This fallback
172+
ensures that the raw data is still accessible.
156173
"""
157174
if (
158175
column_name is not None
@@ -171,10 +188,27 @@ def _parse_enum_type(
171188
value: PBValue,
172189
metadata_type: SqlType.Enum,
173190
column_name: str | None,
174-
column_info: dict[str, Any] | None = None,
175-
) -> int | Any:
191+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
192+
) -> int | str:
176193
"""
177-
Parses an integer value into a Protobuf enum.
194+
Parses an integer value into a Protobuf enum name string using type information
195+
provided in column_info.
196+
197+
Args:
198+
value: The value to parse, expected to have an int_value attribute.
199+
metadata_type: The expected SQL type (Enum).
200+
column_name: The name of the column.
201+
column_info: (Optional) A dictionary mapping column names to their
202+
corresponding Protobuf EnumTypeWrapper objects. This information
203+
is used to convert the integer to an enum name.
204+
205+
Returns:
206+
A string representing the name of the enum value if conversion is successful.
207+
If conversion fails for any reason, such as the required EnumTypeWrapper
208+
not being found in column_info, or if an error occurs during the name lookup
209+
(e.g., the integer is not a valid enum value), the function returns the
210+
original integer value (value.int_value). This fallback ensures the
211+
raw integer representation is still accessible.
178212
"""
179213
if (
180214
column_name is not None
@@ -187,9 +221,12 @@ def _parse_enum_type(
187221
return value.int_value
188222

189223

190-
_TYPE_PARSERS: Dict[
191-
Type[SqlType.Type], Callable[[PBValue, Any, str | None, dict[str, Any] | None], Any]
192-
] = {
224+
ParserCallable: TypeAlias = Callable[
225+
[PBValue, Any, Optional[str], Optional[Dict[str, Union[Message, EnumTypeWrapper]]]],
226+
Any,
227+
]
228+
229+
_TYPE_PARSERS: Dict[Type[SqlType.Type], ParserCallable] = {
193230
SqlType.Timestamp: _parse_timestamp_type,
194231
SqlType.Struct: _parse_struct_type,
195232
SqlType.Array: _parse_array_type,
@@ -203,7 +240,7 @@ def _parse_pb_value_to_python_value(
203240
value: PBValue,
204241
metadata_type: SqlType.Type,
205242
column_name: str | None,
206-
column_info: dict[str, Any] | None = None,
243+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
207244
) -> Any:
208245
"""
209246
used for converting the value represented as a protobufs to a python object.

google/cloud/bigtable/data/execute_query/_reader.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,17 @@
1414
from __future__ import annotations
1515

1616
from typing import (
17-
Any,
1817
List,
1918
TypeVar,
2019
Generic,
2120
Iterable,
2221
Optional,
2322
Sequence,
23+
Union,
2424
)
2525
from abc import ABC, abstractmethod
26+
from google.protobuf.message import Message
27+
from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper
2628

2729
from google.cloud.bigtable_v2 import ProtoRows, Value as PBValue
2830

@@ -59,7 +61,7 @@ def consume(
5961
self,
6062
batches_to_consume: List[bytes],
6163
metadata: Metadata,
62-
column_info: dict[str, Any] | None = None,
64+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
6365
) -> Optional[Iterable[T]]:
6466
"""This method receives a list of batches of bytes to be parsed as ProtoRows messages.
6567
It then uses the metadata to group the values in the parsed messages into rows. Returns
@@ -99,7 +101,7 @@ def _construct_query_result_row(
99101
self,
100102
values: Sequence[PBValue],
101103
metadata: Metadata,
102-
column_info: dict[str, Any] | None = None,
104+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
103105
) -> QueryResultRow:
104106
result = QueryResultRow()
105107
columns = metadata.columns
@@ -119,7 +121,7 @@ def consume(
119121
self,
120122
batches_to_consume: List[bytes],
121123
metadata: Metadata,
122-
column_info: dict[str, Any] | None = None,
124+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
123125
) -> Optional[Iterable[QueryResultRow]]:
124126
num_columns = len(metadata.columns)
125127
rows = []

google/cloud/bigtable/data/execute_query/_sync_autogen/execute_query_iterator.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
# This file is automatically generated by CrossSync. Do not edit manually.
1717

1818
from __future__ import annotations
19-
from typing import Any, Dict, Optional, Sequence, Tuple, TYPE_CHECKING
19+
from typing import Any, Dict, Optional, Sequence, Tuple, TYPE_CHECKING, Union
2020
from google.api_core import retry as retries
21+
from google.protobuf.message import Message
22+
from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper
2123
from google.cloud.bigtable.data.execute_query._byte_cursor import _ByteCursor
2224
from google.cloud.bigtable.data._helpers import (
2325
_attempt_timeout_generator,
@@ -63,7 +65,7 @@ def __init__(
6365
operation_timeout: float,
6466
req_metadata: Sequence[Tuple[str, str]] = (),
6567
retryable_excs: Sequence[type[Exception]] = (),
66-
column_info: Dict[str, Any] | None = None,
68+
column_info: dict[str, Union[Message, EnumTypeWrapper]] | None = None,
6769
) -> None:
6870
"""Collects responses from ExecuteQuery requests and parses them into QueryResultRows.
6971

0 commit comments

Comments
 (0)