google-cloud-bigtable 2.30.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google/cloud/bigtable/__init__.py +25 -0
- google/cloud/bigtable/app_profile.py +377 -0
- google/cloud/bigtable/backup.py +490 -0
- google/cloud/bigtable/batcher.py +414 -0
- google/cloud/bigtable/client.py +475 -0
- google/cloud/bigtable/cluster.py +541 -0
- google/cloud/bigtable/column_family.py +362 -0
- google/cloud/bigtable/data/__init__.py +103 -0
- google/cloud/bigtable/data/_async/__init__.py +25 -0
- google/cloud/bigtable/data/_async/_mutate_rows.py +226 -0
- google/cloud/bigtable/data/_async/_read_rows.py +363 -0
- google/cloud/bigtable/data/_async/client.py +1522 -0
- google/cloud/bigtable/data/_async/mutations_batcher.py +534 -0
- google/cloud/bigtable/data/_cross_sync/__init__.py +20 -0
- google/cloud/bigtable/data/_cross_sync/_decorators.py +441 -0
- google/cloud/bigtable/data/_cross_sync/_mapping_meta.py +64 -0
- google/cloud/bigtable/data/_cross_sync/cross_sync.py +334 -0
- google/cloud/bigtable/data/_helpers.py +249 -0
- google/cloud/bigtable/data/_sync_autogen/_mutate_rows.py +182 -0
- google/cloud/bigtable/data/_sync_autogen/_read_rows.py +304 -0
- google/cloud/bigtable/data/_sync_autogen/client.py +1293 -0
- google/cloud/bigtable/data/_sync_autogen/mutations_batcher.py +449 -0
- google/cloud/bigtable/data/exceptions.py +340 -0
- google/cloud/bigtable/data/execute_query/__init__.py +43 -0
- google/cloud/bigtable/data/execute_query/_async/__init__.py +13 -0
- google/cloud/bigtable/data/execute_query/_async/execute_query_iterator.py +291 -0
- google/cloud/bigtable/data/execute_query/_byte_cursor.py +123 -0
- google/cloud/bigtable/data/execute_query/_checksum.py +43 -0
- google/cloud/bigtable/data/execute_query/_parameters_formatting.py +155 -0
- google/cloud/bigtable/data/execute_query/_query_result_parsing_utils.py +134 -0
- google/cloud/bigtable/data/execute_query/_reader.py +122 -0
- google/cloud/bigtable/data/execute_query/_sync_autogen/execute_query_iterator.py +239 -0
- google/cloud/bigtable/data/execute_query/metadata.py +399 -0
- google/cloud/bigtable/data/execute_query/values.py +123 -0
- google/cloud/bigtable/data/mutations.py +380 -0
- google/cloud/bigtable/data/read_modify_write_rules.py +112 -0
- google/cloud/bigtable/data/read_rows_query.py +536 -0
- google/cloud/bigtable/data/row.py +535 -0
- google/cloud/bigtable/data/row_filters.py +968 -0
- google/cloud/bigtable/encryption_info.py +64 -0
- google/cloud/bigtable/enums.py +223 -0
- google/cloud/bigtable/error.py +64 -0
- google/cloud/bigtable/gapic_version.py +16 -0
- google/cloud/bigtable/helpers.py +31 -0
- google/cloud/bigtable/instance.py +789 -0
- google/cloud/bigtable/policy.py +255 -0
- google/cloud/bigtable/row.py +1267 -0
- google/cloud/bigtable/row_data.py +380 -0
- google/cloud/bigtable/row_filters.py +838 -0
- google/cloud/bigtable/row_merger.py +250 -0
- google/cloud/bigtable/row_set.py +213 -0
- google/cloud/bigtable/table.py +1409 -0
- google/cloud/bigtable_admin/__init__.py +410 -0
- google/cloud/bigtable_admin/gapic_version.py +16 -0
- google/cloud/bigtable_admin/py.typed +2 -0
- google/cloud/bigtable_admin_v2/__init__.py +250 -0
- google/cloud/bigtable_admin_v2/gapic_metadata.json +962 -0
- google/cloud/bigtable_admin_v2/gapic_version.py +16 -0
- google/cloud/bigtable_admin_v2/py.typed +2 -0
- google/cloud/bigtable_admin_v2/services/__init__.py +15 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/__init__.py +22 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/async_client.py +3465 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py +3944 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/pagers.py +681 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/__init__.py +40 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py +751 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py +1249 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py +1579 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest.py +6820 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest_base.py +1746 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/__init__.py +22 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py +3472 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py +3949 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/pagers.py +669 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/__init__.py +40 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py +708 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py +1257 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py +1549 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest.py +6609 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest_base.py +1714 -0
- google/cloud/bigtable_admin_v2/types/__init__.py +248 -0
- google/cloud/bigtable_admin_v2/types/bigtable_instance_admin.py +1364 -0
- google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py +1715 -0
- google/cloud/bigtable_admin_v2/types/common.py +81 -0
- google/cloud/bigtable_admin_v2/types/instance.py +819 -0
- google/cloud/bigtable_admin_v2/types/table.py +1028 -0
- google/cloud/bigtable_admin_v2/types/types.py +776 -0
- google/cloud/bigtable_v2/__init__.py +136 -0
- google/cloud/bigtable_v2/gapic_metadata.json +193 -0
- google/cloud/bigtable_v2/gapic_version.py +16 -0
- google/cloud/bigtable_v2/py.typed +2 -0
- google/cloud/bigtable_v2/services/__init__.py +15 -0
- google/cloud/bigtable_v2/services/bigtable/__init__.py +22 -0
- google/cloud/bigtable_v2/services/bigtable/async_client.py +1741 -0
- google/cloud/bigtable_v2/services/bigtable/client.py +2194 -0
- google/cloud/bigtable_v2/services/bigtable/transports/__init__.py +38 -0
- google/cloud/bigtable_v2/services/bigtable/transports/base.py +343 -0
- google/cloud/bigtable_v2/services/bigtable/transports/grpc.py +660 -0
- google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py +762 -0
- google/cloud/bigtable_v2/services/bigtable/transports/rest.py +2489 -0
- google/cloud/bigtable_v2/services/bigtable/transports/rest_base.py +711 -0
- google/cloud/bigtable_v2/types/__init__.py +138 -0
- google/cloud/bigtable_v2/types/bigtable.py +1531 -0
- google/cloud/bigtable_v2/types/data.py +1612 -0
- google/cloud/bigtable_v2/types/feature_flags.py +119 -0
- google/cloud/bigtable_v2/types/request_stats.py +171 -0
- google/cloud/bigtable_v2/types/response_params.py +64 -0
- google/cloud/bigtable_v2/types/types.py +579 -0
- google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_admin_v2_keywords.py +233 -0
- google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_v2_keywords.py +186 -0
- google_cloud_bigtable-2.30.0.dist-info/LICENSE +202 -0
- google_cloud_bigtable-2.30.0.dist-info/METADATA +154 -0
- google_cloud_bigtable-2.30.0.dist-info/RECORD +115 -0
- google_cloud_bigtable-2.30.0.dist-info/WHEEL +5 -0
- google_cloud_bigtable-2.30.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from typing import List, Optional
|
|
16
|
+
|
|
17
|
+
from google.cloud.bigtable.data.execute_query._checksum import _CRC32C
|
|
18
|
+
from google.cloud.bigtable_v2 import ExecuteQueryResponse
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _ByteCursor:
|
|
22
|
+
"""
|
|
23
|
+
Buffers bytes from `ExecuteQuery` responses until resume_token is received or end-of-stream
|
|
24
|
+
is reached. :class:`google.cloud.bigtable_v2.types.bigtable.ExecuteQueryResponse` obtained from
|
|
25
|
+
the server should be passed to the ``consume`` method and its non-None results should be passed
|
|
26
|
+
to appropriate :class:`google.cloud.bigtable.execute_query_reader._Reader` for parsing gathered
|
|
27
|
+
bytes.
|
|
28
|
+
|
|
29
|
+
This class consumes data obtained externally to be usable in both sync and async clients.
|
|
30
|
+
|
|
31
|
+
See :class:`google.cloud.bigtable.execute_query_reader._Reader` for more context.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self):
|
|
35
|
+
self._batch_buffer = bytearray()
|
|
36
|
+
self._batches: List[bytes] = []
|
|
37
|
+
self._resume_token = None
|
|
38
|
+
|
|
39
|
+
def reset(self):
|
|
40
|
+
self._batch_buffer = bytearray()
|
|
41
|
+
self._batches = []
|
|
42
|
+
|
|
43
|
+
def prepare_for_new_request(self):
|
|
44
|
+
"""
|
|
45
|
+
Prepares this ``_ByteCursor`` for retrying an ``ExecuteQuery`` request.
|
|
46
|
+
|
|
47
|
+
Clears internal buffers of this ``_ByteCursor`` and returns last received
|
|
48
|
+
``resume_token`` to be used in retried request.
|
|
49
|
+
|
|
50
|
+
This is the only method that returns ``resume_token`` to the user.
|
|
51
|
+
Returning the token to the user is tightly coupled with clearing internal
|
|
52
|
+
buffers to prevent accidental retry without clearing the state, what would
|
|
53
|
+
cause invalid results. ``resume_token`` are not needed in other cases,
|
|
54
|
+
thus they is no separate getter for it.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
bytes: Last received resume_token.
|
|
58
|
+
"""
|
|
59
|
+
# The first response of any retried stream will always contain reset, so
|
|
60
|
+
# this isn't actually necessary, but we do it for safety
|
|
61
|
+
self.reset()
|
|
62
|
+
return self._resume_token
|
|
63
|
+
|
|
64
|
+
def empty(self) -> bool:
|
|
65
|
+
return not self._batch_buffer and not self._batches
|
|
66
|
+
|
|
67
|
+
def consume(self, response: ExecuteQueryResponse) -> Optional[List[bytes]]:
|
|
68
|
+
"""
|
|
69
|
+
Reads results bytes from an ``ExecuteQuery`` response and adds them to a buffer.
|
|
70
|
+
|
|
71
|
+
If the response contains a ``resume_token``:
|
|
72
|
+
- the ``resume_token`` is saved in this ``_ByteCursor``, and
|
|
73
|
+
- internal buffers are flushed and returned to the caller.
|
|
74
|
+
|
|
75
|
+
``resume_token`` is not available directly, but can be retrieved by calling
|
|
76
|
+
:meth:`._ByteCursor.prepare_for_new_request` when preparing to retry a request.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
response (google.cloud.bigtable_v2.types.bigtable.ExecuteQueryResponse):
|
|
80
|
+
Response obtained from the stream.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
bytes or None: List of bytes if buffers were flushed or None otherwise.
|
|
84
|
+
Each element in the list represents the bytes of a `ProtoRows` message.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
ValueError: If provided ``ExecuteQueryResponse`` is not valid
|
|
88
|
+
or contains bytes representing response of a different kind than previously
|
|
89
|
+
processed responses.
|
|
90
|
+
"""
|
|
91
|
+
response_pb = response._pb # proto-plus attribute retrieval is slow.
|
|
92
|
+
|
|
93
|
+
if response_pb.HasField("results"):
|
|
94
|
+
results = response_pb.results
|
|
95
|
+
if results.reset:
|
|
96
|
+
self.reset()
|
|
97
|
+
if results.HasField("proto_rows_batch"):
|
|
98
|
+
self._batch_buffer.extend(results.proto_rows_batch.batch_data)
|
|
99
|
+
# Note that 0 is a valid checksum so we must check for field presence
|
|
100
|
+
if results.HasField("batch_checksum"):
|
|
101
|
+
expected_checksum = results.batch_checksum
|
|
102
|
+
checksum = _CRC32C.checksum(self._batch_buffer)
|
|
103
|
+
if expected_checksum != checksum:
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"Unexpected checksum mismatch. Expected: {expected_checksum}, got: {checksum}"
|
|
106
|
+
)
|
|
107
|
+
# We have a complete batch so we move it to batches and reset the
|
|
108
|
+
# batch_buffer
|
|
109
|
+
self._batches.append(memoryview(self._batch_buffer))
|
|
110
|
+
self._batch_buffer = bytearray()
|
|
111
|
+
|
|
112
|
+
if results.resume_token:
|
|
113
|
+
self._resume_token = results.resume_token
|
|
114
|
+
|
|
115
|
+
if self._batches:
|
|
116
|
+
if self._batch_buffer:
|
|
117
|
+
raise ValueError("Unexpected resume_token without checksum")
|
|
118
|
+
return_value = self._batches
|
|
119
|
+
self._batches = []
|
|
120
|
+
return return_value
|
|
121
|
+
else:
|
|
122
|
+
raise ValueError(f"Unexpected ExecuteQueryResponse: {response}")
|
|
123
|
+
return None
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright 2025 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
import warnings
|
|
17
|
+
|
|
18
|
+
with warnings.catch_warnings(record=True) as import_warning:
|
|
19
|
+
import google_crc32c # type: ignore
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _CRC32C(object):
|
|
23
|
+
"""
|
|
24
|
+
Wrapper around ``google_crc32c`` library
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
warn_emitted = False
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def checksum(cls, val: bytearray) -> int:
|
|
31
|
+
"""
|
|
32
|
+
Returns the crc32c checksum of the data.
|
|
33
|
+
"""
|
|
34
|
+
if import_warning and not cls.warn_emitted:
|
|
35
|
+
cls.warn_emitted = True
|
|
36
|
+
warnings.warn(
|
|
37
|
+
"Using pure python implementation of `google-crc32` for ExecuteQuery response "
|
|
38
|
+
"validation. This is significantly slower than the c extension. If possible, "
|
|
39
|
+
"run in an environment that supports the c extension.",
|
|
40
|
+
RuntimeWarning,
|
|
41
|
+
)
|
|
42
|
+
memory_view = memoryview(val)
|
|
43
|
+
return google_crc32c.value(bytes(memory_view))
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import datetime
|
|
16
|
+
from typing import Any, Dict, Optional
|
|
17
|
+
|
|
18
|
+
from google.api_core.datetime_helpers import DatetimeWithNanoseconds
|
|
19
|
+
|
|
20
|
+
from google.cloud.bigtable.data.exceptions import ParameterTypeInferenceFailed
|
|
21
|
+
from google.cloud.bigtable.data.execute_query.metadata import SqlType
|
|
22
|
+
from google.cloud.bigtable.data.execute_query.values import ExecuteQueryValueType
|
|
23
|
+
from google.cloud.bigtable_v2.types.data import Value
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _format_execute_query_params(
|
|
27
|
+
params: Optional[Dict[str, ExecuteQueryValueType]],
|
|
28
|
+
parameter_types: Optional[Dict[str, SqlType.Type]],
|
|
29
|
+
) -> Dict[str, Value]:
|
|
30
|
+
"""
|
|
31
|
+
Takes a dictionary of param_name -> param_value and optionally parameter types.
|
|
32
|
+
If the parameters types are not provided, this function tries to infer them.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
params (Optional[Dict[str, ExecuteQueryValueType]]): mapping from parameter names
|
|
36
|
+
like they appear in query (without @ at the beginning) to their values.
|
|
37
|
+
Only values of type ExecuteQueryValueType are permitted.
|
|
38
|
+
parameter_types (Optional[Dict[str, SqlType.Type]]): mapping of parameter names
|
|
39
|
+
to their types.
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
ValueError: raised when parameter types cannot be inferred and were not
|
|
43
|
+
provided explicitly.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
dictionary prasable to a protobuf represenging parameters as defined
|
|
47
|
+
in ExecuteQueryRequest.params
|
|
48
|
+
"""
|
|
49
|
+
if not params:
|
|
50
|
+
return {}
|
|
51
|
+
parameter_types = parameter_types or {}
|
|
52
|
+
|
|
53
|
+
result_values = {}
|
|
54
|
+
for key, value in params.items():
|
|
55
|
+
user_provided_type = parameter_types.get(key)
|
|
56
|
+
try:
|
|
57
|
+
if user_provided_type:
|
|
58
|
+
if not isinstance(user_provided_type, SqlType.Type):
|
|
59
|
+
raise ValueError(
|
|
60
|
+
f"Parameter type for {key} should be provided as an instance of SqlType.Type subclass."
|
|
61
|
+
)
|
|
62
|
+
param_type = user_provided_type
|
|
63
|
+
else:
|
|
64
|
+
param_type = _detect_type(value)
|
|
65
|
+
|
|
66
|
+
value_pb_dict = _convert_value_to_pb_value_dict(value, param_type)
|
|
67
|
+
except ValueError as err:
|
|
68
|
+
raise ValueError(f"Error when parsing parameter {key}") from err
|
|
69
|
+
result_values[key] = value_pb_dict
|
|
70
|
+
|
|
71
|
+
return result_values
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _to_param_types(
|
|
75
|
+
params: Optional[Dict[str, ExecuteQueryValueType]],
|
|
76
|
+
param_types: Optional[Dict[str, SqlType.Type]],
|
|
77
|
+
) -> Dict[str, Dict[str, Any]]:
|
|
78
|
+
"""
|
|
79
|
+
Takes the params and user supplied types and creates a param_type dict for the PrepareQuery api
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
params: Dict of param name to param value
|
|
83
|
+
param_types: Dict of param name to param type for params with types that cannot be inferred
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Dict containing the param name and type for each parameter
|
|
87
|
+
"""
|
|
88
|
+
if params is None:
|
|
89
|
+
return {}
|
|
90
|
+
formatted_types = {}
|
|
91
|
+
for param_key, param_value in params.items():
|
|
92
|
+
if param_types and param_key in param_types:
|
|
93
|
+
formatted_types[param_key] = param_types[param_key]._to_type_pb_dict()
|
|
94
|
+
else:
|
|
95
|
+
formatted_types[param_key] = _detect_type(param_value)._to_type_pb_dict()
|
|
96
|
+
return formatted_types
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _convert_value_to_pb_value_dict(
|
|
100
|
+
value: ExecuteQueryValueType, param_type: SqlType.Type
|
|
101
|
+
) -> Any:
|
|
102
|
+
"""
|
|
103
|
+
Takes a value and converts it to a dictionary parsable to a protobuf.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
value (ExecuteQueryValueType): value
|
|
107
|
+
param_type (SqlType.Type): object describing which ExecuteQuery type the value represents.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
dictionary parsable to a protobuf.
|
|
111
|
+
"""
|
|
112
|
+
# type field will be set only in top-level Value.
|
|
113
|
+
value_dict = param_type._to_value_pb_dict(value)
|
|
114
|
+
value_dict["type_"] = param_type._to_type_pb_dict()
|
|
115
|
+
return value_dict
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
_TYPES_TO_TYPE_DICTS = [
|
|
119
|
+
(bytes, SqlType.Bytes()),
|
|
120
|
+
(str, SqlType.String()),
|
|
121
|
+
(bool, SqlType.Bool()),
|
|
122
|
+
(int, SqlType.Int64()),
|
|
123
|
+
(DatetimeWithNanoseconds, SqlType.Timestamp()),
|
|
124
|
+
(datetime.datetime, SqlType.Timestamp()),
|
|
125
|
+
(datetime.date, SqlType.Date()),
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _detect_type(value: ExecuteQueryValueType) -> SqlType.Type:
|
|
130
|
+
"""
|
|
131
|
+
Infers the ExecuteQuery type based on value. Raises error if type is amiguous.
|
|
132
|
+
raises ParameterTypeInferenceFailed if not possible.
|
|
133
|
+
"""
|
|
134
|
+
if value is None:
|
|
135
|
+
raise ParameterTypeInferenceFailed(
|
|
136
|
+
"Cannot infer type of None, please provide the type manually."
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if isinstance(value, list):
|
|
140
|
+
raise ParameterTypeInferenceFailed(
|
|
141
|
+
"Cannot infer type of ARRAY parameters, please provide the type manually."
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if isinstance(value, float):
|
|
145
|
+
raise ParameterTypeInferenceFailed(
|
|
146
|
+
"Cannot infer type of float, must specify either FLOAT32 or FLOAT64 type manually."
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
for field_type, type_dict in _TYPES_TO_TYPE_DICTS:
|
|
150
|
+
if isinstance(value, field_type):
|
|
151
|
+
return type_dict
|
|
152
|
+
|
|
153
|
+
raise ParameterTypeInferenceFailed(
|
|
154
|
+
f"Cannot infer type of {type(value).__name__}, please provide the type manually."
|
|
155
|
+
)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from typing import Any, Callable, Dict, Type
|
|
16
|
+
from google.cloud.bigtable.data.execute_query.values import Struct
|
|
17
|
+
from google.cloud.bigtable.data.execute_query.metadata import SqlType
|
|
18
|
+
from google.cloud.bigtable_v2 import Value as PBValue
|
|
19
|
+
from google.api_core.datetime_helpers import DatetimeWithNanoseconds
|
|
20
|
+
|
|
21
|
+
_REQUIRED_PROTO_FIELDS = {
|
|
22
|
+
SqlType.Bytes: "bytes_value",
|
|
23
|
+
SqlType.String: "string_value",
|
|
24
|
+
SqlType.Int64: "int_value",
|
|
25
|
+
SqlType.Float32: "float_value",
|
|
26
|
+
SqlType.Float64: "float_value",
|
|
27
|
+
SqlType.Bool: "bool_value",
|
|
28
|
+
SqlType.Timestamp: "timestamp_value",
|
|
29
|
+
SqlType.Date: "date_value",
|
|
30
|
+
SqlType.Struct: "array_value",
|
|
31
|
+
SqlType.Array: "array_value",
|
|
32
|
+
SqlType.Map: "array_value",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _parse_array_type(value: PBValue, metadata_type: SqlType.Array) -> Any:
|
|
37
|
+
"""
|
|
38
|
+
used for parsing an array represented as a protobuf to a python list.
|
|
39
|
+
"""
|
|
40
|
+
return list(
|
|
41
|
+
map(
|
|
42
|
+
lambda val: _parse_pb_value_to_python_value(
|
|
43
|
+
val, metadata_type.element_type
|
|
44
|
+
),
|
|
45
|
+
value.array_value.values,
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _parse_map_type(value: PBValue, metadata_type: SqlType.Map) -> Any:
|
|
51
|
+
"""
|
|
52
|
+
used for parsing a map represented as a protobuf to a python dict.
|
|
53
|
+
|
|
54
|
+
Values of type `Map` are stored in a `Value.array_value` where each entry
|
|
55
|
+
is another `Value.array_value` with two elements (the key and the value,
|
|
56
|
+
in that order).
|
|
57
|
+
Normally encoded Map values won't have repeated keys, however, the client
|
|
58
|
+
must handle the case in which they do. If the same key appears
|
|
59
|
+
multiple times, the _last_ value takes precedence.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
return dict(
|
|
64
|
+
map(
|
|
65
|
+
lambda map_entry: (
|
|
66
|
+
_parse_pb_value_to_python_value(
|
|
67
|
+
map_entry.array_value.values[0], metadata_type.key_type
|
|
68
|
+
),
|
|
69
|
+
_parse_pb_value_to_python_value(
|
|
70
|
+
map_entry.array_value.values[1], metadata_type.value_type
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
value.array_value.values,
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
except IndexError:
|
|
77
|
+
raise ValueError("Invalid map entry - less or more than two values.")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _parse_struct_type(value: PBValue, metadata_type: SqlType.Struct) -> Struct:
|
|
81
|
+
"""
|
|
82
|
+
used for parsing a struct represented as a protobuf to a
|
|
83
|
+
google.cloud.bigtable.data.execute_query.Struct
|
|
84
|
+
"""
|
|
85
|
+
if len(value.array_value.values) != len(metadata_type.fields):
|
|
86
|
+
raise ValueError("Mismatched lengths of values and types.")
|
|
87
|
+
|
|
88
|
+
struct = Struct()
|
|
89
|
+
for value, field in zip(value.array_value.values, metadata_type.fields):
|
|
90
|
+
field_name, field_type = field
|
|
91
|
+
struct.add_field(field_name, _parse_pb_value_to_python_value(value, field_type))
|
|
92
|
+
|
|
93
|
+
return struct
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _parse_timestamp_type(
|
|
97
|
+
value: PBValue, metadata_type: SqlType.Timestamp
|
|
98
|
+
) -> DatetimeWithNanoseconds:
|
|
99
|
+
"""
|
|
100
|
+
used for parsing a timestamp represented as a protobuf to DatetimeWithNanoseconds
|
|
101
|
+
"""
|
|
102
|
+
return DatetimeWithNanoseconds.from_timestamp_pb(value.timestamp_value)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
_TYPE_PARSERS: Dict[Type[SqlType.Type], Callable[[PBValue, Any], Any]] = {
|
|
106
|
+
SqlType.Timestamp: _parse_timestamp_type,
|
|
107
|
+
SqlType.Struct: _parse_struct_type,
|
|
108
|
+
SqlType.Array: _parse_array_type,
|
|
109
|
+
SqlType.Map: _parse_map_type,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _parse_pb_value_to_python_value(value: PBValue, metadata_type: SqlType.Type) -> Any:
|
|
114
|
+
"""
|
|
115
|
+
used for converting the value represented as a protobufs to a python object.
|
|
116
|
+
"""
|
|
117
|
+
value_kind = value.WhichOneof("kind")
|
|
118
|
+
if not value_kind:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
kind = type(metadata_type)
|
|
122
|
+
if not value.HasField(_REQUIRED_PROTO_FIELDS[kind]):
|
|
123
|
+
raise ValueError(
|
|
124
|
+
f"{_REQUIRED_PROTO_FIELDS[kind]} field for {kind.__name__} type not found in a Value."
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if kind in _TYPE_PARSERS:
|
|
128
|
+
parser = _TYPE_PARSERS[kind]
|
|
129
|
+
return parser(value, metadata_type)
|
|
130
|
+
elif kind in _REQUIRED_PROTO_FIELDS:
|
|
131
|
+
field_name = _REQUIRED_PROTO_FIELDS[kind]
|
|
132
|
+
return getattr(value, field_name)
|
|
133
|
+
else:
|
|
134
|
+
raise ValueError(f"Unknown kind {kind}")
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from typing import (
|
|
16
|
+
List,
|
|
17
|
+
TypeVar,
|
|
18
|
+
Generic,
|
|
19
|
+
Iterable,
|
|
20
|
+
Optional,
|
|
21
|
+
Sequence,
|
|
22
|
+
)
|
|
23
|
+
from abc import ABC, abstractmethod
|
|
24
|
+
|
|
25
|
+
from google.cloud.bigtable_v2 import ProtoRows, Value as PBValue
|
|
26
|
+
|
|
27
|
+
from google.cloud.bigtable.data.execute_query._query_result_parsing_utils import (
|
|
28
|
+
_parse_pb_value_to_python_value,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from google.cloud.bigtable.helpers import batched
|
|
32
|
+
|
|
33
|
+
from google.cloud.bigtable.data.execute_query.values import QueryResultRow
|
|
34
|
+
from google.cloud.bigtable.data.execute_query.metadata import Metadata
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
T = TypeVar("T")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class _Reader(ABC, Generic[T]):
|
|
41
|
+
"""
|
|
42
|
+
An interface for classes that consume and parse bytes returned by ``_ByteCursor``.
|
|
43
|
+
Parsed bytes should be gathered into bundles (rows or columns) of expected size
|
|
44
|
+
and converted to an appropriate type ``T`` that will be returned as a semantically
|
|
45
|
+
meaningful result to the library user by
|
|
46
|
+
:meth:`google.cloud.bigtable.instance.Instance.execute_query` or
|
|
47
|
+
:meth:`google.cloud.bigtable.data._async.client.BigtableDataClientAsync.execute_query`
|
|
48
|
+
methods.
|
|
49
|
+
|
|
50
|
+
This class consumes data obtained externally to be usable in both sync and async clients.
|
|
51
|
+
|
|
52
|
+
See :class:`google.cloud.bigtable.byte_cursor._ByteCursor` for more context.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def consume(
|
|
57
|
+
self, batches_to_consume: List[bytes], metadata: Metadata
|
|
58
|
+
) -> Optional[Iterable[T]]:
|
|
59
|
+
"""This method receives a list of batches of bytes to be parsed as ProtoRows messages.
|
|
60
|
+
It then uses the metadata to group the values in the parsed messages into rows. Returns
|
|
61
|
+
None if batches_to_consume is empty
|
|
62
|
+
Args:
|
|
63
|
+
bytes_to_consume (bytes): chunk of parsable byte batches received from
|
|
64
|
+
:meth:`google.cloud.bigtable.byte_cursor._ByteCursor.consume`
|
|
65
|
+
method.
|
|
66
|
+
metadata: metadata used to transform values to rows
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Iterable[T] or None: Iterable if gathered values can form one or more instances of T,
|
|
70
|
+
or None if there is not enough data to construct at least one instance of T with
|
|
71
|
+
appropriate number of entries.
|
|
72
|
+
"""
|
|
73
|
+
raise NotImplementedError
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class _QueryResultRowReader(_Reader[QueryResultRow]):
|
|
77
|
+
"""
|
|
78
|
+
A :class:`._Reader` consuming bytes representing
|
|
79
|
+
:class:`google.cloud.bigtable_v2.types.Type`
|
|
80
|
+
and producing :class:`google.cloud.bigtable.execute_query.QueryResultRow`.
|
|
81
|
+
|
|
82
|
+
Number of entries in each row is determined by number of columns in
|
|
83
|
+
:class:`google.cloud.bigtable.execute_query.Metadata` obtained from
|
|
84
|
+
:class:`google.cloud.bigtable.byte_cursor._ByteCursor` passed in the constructor.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def _parse_proto_rows(self, bytes_to_parse: bytes) -> Iterable[PBValue]:
|
|
88
|
+
proto_rows = ProtoRows.pb().FromString(bytes_to_parse)
|
|
89
|
+
return proto_rows.values
|
|
90
|
+
|
|
91
|
+
def _construct_query_result_row(
|
|
92
|
+
self, values: Sequence[PBValue], metadata: Metadata
|
|
93
|
+
) -> QueryResultRow:
|
|
94
|
+
result = QueryResultRow()
|
|
95
|
+
columns = metadata.columns
|
|
96
|
+
|
|
97
|
+
assert len(values) == len(
|
|
98
|
+
columns
|
|
99
|
+
), "This function should be called only when count of values matches count of columns."
|
|
100
|
+
|
|
101
|
+
for column, value in zip(columns, values):
|
|
102
|
+
parsed_value = _parse_pb_value_to_python_value(value, column.column_type)
|
|
103
|
+
result.add_field(column.column_name, parsed_value)
|
|
104
|
+
return result
|
|
105
|
+
|
|
106
|
+
def consume(
|
|
107
|
+
self, batches_to_consume: List[bytes], metadata: Metadata
|
|
108
|
+
) -> Optional[Iterable[QueryResultRow]]:
|
|
109
|
+
num_columns = len(metadata.columns)
|
|
110
|
+
rows = []
|
|
111
|
+
for batch_bytes in batches_to_consume:
|
|
112
|
+
values = self._parse_proto_rows(batch_bytes)
|
|
113
|
+
for row_data in batched(values, n=num_columns):
|
|
114
|
+
if len(row_data) == num_columns:
|
|
115
|
+
rows.append(self._construct_query_result_row(row_data, metadata))
|
|
116
|
+
else:
|
|
117
|
+
raise ValueError(
|
|
118
|
+
"Unexpected error, recieved bad number of values. "
|
|
119
|
+
f"Expected {num_columns} got {len(row_data)}."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return rows
|