google-cloud-spanner 3.55.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.
Files changed (119) hide show
  1. google/cloud/spanner.py +47 -0
  2. google/cloud/spanner_admin_database_v1/__init__.py +146 -0
  3. google/cloud/spanner_admin_database_v1/gapic_metadata.json +418 -0
  4. google/cloud/spanner_admin_database_v1/gapic_version.py +16 -0
  5. google/cloud/spanner_admin_database_v1/py.typed +2 -0
  6. google/cloud/spanner_admin_database_v1/services/__init__.py +15 -0
  7. google/cloud/spanner_admin_database_v1/services/database_admin/__init__.py +22 -0
  8. google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py +4097 -0
  9. google/cloud/spanner_admin_database_v1/services/database_admin/client.py +4602 -0
  10. google/cloud/spanner_admin_database_v1/services/database_admin/pagers.py +989 -0
  11. google/cloud/spanner_admin_database_v1/services/database_admin/transports/__init__.py +38 -0
  12. google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py +820 -0
  13. google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py +1303 -0
  14. google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py +1688 -0
  15. google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py +6512 -0
  16. google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py +1650 -0
  17. google/cloud/spanner_admin_database_v1/types/__init__.py +144 -0
  18. google/cloud/spanner_admin_database_v1/types/backup.py +1106 -0
  19. google/cloud/spanner_admin_database_v1/types/backup_schedule.py +369 -0
  20. google/cloud/spanner_admin_database_v1/types/common.py +180 -0
  21. google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py +1303 -0
  22. google/cloud/spanner_admin_instance_v1/__init__.py +110 -0
  23. google/cloud/spanner_admin_instance_v1/gapic_metadata.json +343 -0
  24. google/cloud/spanner_admin_instance_v1/gapic_version.py +16 -0
  25. google/cloud/spanner_admin_instance_v1/py.typed +2 -0
  26. google/cloud/spanner_admin_instance_v1/services/__init__.py +15 -0
  27. google/cloud/spanner_admin_instance_v1/services/instance_admin/__init__.py +22 -0
  28. google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py +3466 -0
  29. google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py +3881 -0
  30. google/cloud/spanner_admin_instance_v1/services/instance_admin/pagers.py +856 -0
  31. google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/__init__.py +38 -0
  32. google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py +545 -0
  33. google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py +1347 -0
  34. google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py +1539 -0
  35. google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py +4834 -0
  36. google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py +1198 -0
  37. google/cloud/spanner_admin_instance_v1/types/__init__.py +104 -0
  38. google/cloud/spanner_admin_instance_v1/types/common.py +99 -0
  39. google/cloud/spanner_admin_instance_v1/types/spanner_instance_admin.py +2375 -0
  40. google/cloud/spanner_dbapi/__init__.py +93 -0
  41. google/cloud/spanner_dbapi/_helpers.py +113 -0
  42. google/cloud/spanner_dbapi/batch_dml_executor.py +135 -0
  43. google/cloud/spanner_dbapi/checksum.py +80 -0
  44. google/cloud/spanner_dbapi/client_side_statement_executor.py +140 -0
  45. google/cloud/spanner_dbapi/client_side_statement_parser.py +106 -0
  46. google/cloud/spanner_dbapi/connection.py +818 -0
  47. google/cloud/spanner_dbapi/cursor.py +609 -0
  48. google/cloud/spanner_dbapi/exceptions.py +172 -0
  49. google/cloud/spanner_dbapi/parse_utils.py +392 -0
  50. google/cloud/spanner_dbapi/parsed_statement.py +63 -0
  51. google/cloud/spanner_dbapi/parser.py +258 -0
  52. google/cloud/spanner_dbapi/partition_helper.py +41 -0
  53. google/cloud/spanner_dbapi/transaction_helper.py +294 -0
  54. google/cloud/spanner_dbapi/types.py +106 -0
  55. google/cloud/spanner_dbapi/utils.py +147 -0
  56. google/cloud/spanner_dbapi/version.py +20 -0
  57. google/cloud/spanner_v1/__init__.py +154 -0
  58. google/cloud/spanner_v1/_helpers.py +751 -0
  59. google/cloud/spanner_v1/_opentelemetry_tracing.py +165 -0
  60. google/cloud/spanner_v1/backup.py +397 -0
  61. google/cloud/spanner_v1/batch.py +433 -0
  62. google/cloud/spanner_v1/client.py +538 -0
  63. google/cloud/spanner_v1/data_types.py +350 -0
  64. google/cloud/spanner_v1/database.py +1968 -0
  65. google/cloud/spanner_v1/database_sessions_manager.py +249 -0
  66. google/cloud/spanner_v1/gapic_metadata.json +268 -0
  67. google/cloud/spanner_v1/gapic_version.py +16 -0
  68. google/cloud/spanner_v1/instance.py +735 -0
  69. google/cloud/spanner_v1/keyset.py +193 -0
  70. google/cloud/spanner_v1/merged_result_set.py +146 -0
  71. google/cloud/spanner_v1/metrics/constants.py +71 -0
  72. google/cloud/spanner_v1/metrics/metrics_capture.py +75 -0
  73. google/cloud/spanner_v1/metrics/metrics_exporter.py +384 -0
  74. google/cloud/spanner_v1/metrics/metrics_interceptor.py +156 -0
  75. google/cloud/spanner_v1/metrics/metrics_tracer.py +588 -0
  76. google/cloud/spanner_v1/metrics/metrics_tracer_factory.py +328 -0
  77. google/cloud/spanner_v1/metrics/spanner_metrics_tracer_factory.py +172 -0
  78. google/cloud/spanner_v1/param_types.py +110 -0
  79. google/cloud/spanner_v1/pool.py +813 -0
  80. google/cloud/spanner_v1/py.typed +2 -0
  81. google/cloud/spanner_v1/request_id_header.py +64 -0
  82. google/cloud/spanner_v1/services/__init__.py +15 -0
  83. google/cloud/spanner_v1/services/spanner/__init__.py +22 -0
  84. google/cloud/spanner_v1/services/spanner/async_client.py +2205 -0
  85. google/cloud/spanner_v1/services/spanner/client.py +2624 -0
  86. google/cloud/spanner_v1/services/spanner/pagers.py +196 -0
  87. google/cloud/spanner_v1/services/spanner/transports/__init__.py +38 -0
  88. google/cloud/spanner_v1/services/spanner/transports/base.py +520 -0
  89. google/cloud/spanner_v1/services/spanner/transports/grpc.py +911 -0
  90. google/cloud/spanner_v1/services/spanner/transports/grpc_asyncio.py +1144 -0
  91. google/cloud/spanner_v1/services/spanner/transports/rest.py +3468 -0
  92. google/cloud/spanner_v1/services/spanner/transports/rest_base.py +981 -0
  93. google/cloud/spanner_v1/session.py +631 -0
  94. google/cloud/spanner_v1/session_options.py +133 -0
  95. google/cloud/spanner_v1/snapshot.py +1057 -0
  96. google/cloud/spanner_v1/streamed.py +402 -0
  97. google/cloud/spanner_v1/table.py +181 -0
  98. google/cloud/spanner_v1/testing/__init__.py +0 -0
  99. google/cloud/spanner_v1/testing/database_test.py +121 -0
  100. google/cloud/spanner_v1/testing/interceptors.py +118 -0
  101. google/cloud/spanner_v1/testing/mock_database_admin.py +38 -0
  102. google/cloud/spanner_v1/testing/mock_spanner.py +261 -0
  103. google/cloud/spanner_v1/testing/spanner_database_admin_pb2_grpc.py +1267 -0
  104. google/cloud/spanner_v1/testing/spanner_pb2_grpc.py +882 -0
  105. google/cloud/spanner_v1/transaction.py +747 -0
  106. google/cloud/spanner_v1/types/__init__.py +118 -0
  107. google/cloud/spanner_v1/types/commit_response.py +94 -0
  108. google/cloud/spanner_v1/types/keys.py +248 -0
  109. google/cloud/spanner_v1/types/mutation.py +201 -0
  110. google/cloud/spanner_v1/types/query_plan.py +220 -0
  111. google/cloud/spanner_v1/types/result_set.py +379 -0
  112. google/cloud/spanner_v1/types/spanner.py +1815 -0
  113. google/cloud/spanner_v1/types/transaction.py +818 -0
  114. google/cloud/spanner_v1/types/type.py +288 -0
  115. google_cloud_spanner-3.55.0.dist-info/LICENSE +202 -0
  116. google_cloud_spanner-3.55.0.dist-info/METADATA +318 -0
  117. google_cloud_spanner-3.55.0.dist-info/RECORD +119 -0
  118. google_cloud_spanner-3.55.0.dist-info/WHEEL +5 -0
  119. google_cloud_spanner-3.55.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,751 @@
1
+ # Copyright 2016 Google LLC All rights reserved.
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
+ """Helper functions for Cloud Spanner."""
16
+
17
+ import datetime
18
+ import decimal
19
+ import math
20
+ import time
21
+ import base64
22
+ import threading
23
+
24
+ from google.protobuf.struct_pb2 import ListValue
25
+ from google.protobuf.struct_pb2 import Value
26
+ from google.protobuf.message import Message
27
+ from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper
28
+
29
+ from google.api_core import datetime_helpers
30
+ from google.api_core.exceptions import Aborted
31
+ from google.cloud._helpers import _date_from_iso8601_date
32
+ from google.cloud.spanner_v1 import TypeCode
33
+ from google.cloud.spanner_v1 import ExecuteSqlRequest
34
+ from google.cloud.spanner_v1 import JsonObject, Interval
35
+ from google.cloud.spanner_v1 import TransactionOptions
36
+ from google.cloud.spanner_v1.request_id_header import with_request_id
37
+ from google.rpc.error_details_pb2 import RetryInfo
38
+
39
+ try:
40
+ from opentelemetry.propagate import inject
41
+ from opentelemetry.propagators.textmap import Setter
42
+
43
+ HAS_OPENTELEMETRY_INSTALLED = True
44
+ except ImportError:
45
+ HAS_OPENTELEMETRY_INSTALLED = False
46
+ from typing import List, Tuple
47
+ import random
48
+
49
+ # Validation error messages
50
+ NUMERIC_MAX_SCALE_ERR_MSG = (
51
+ "Max scale for a numeric is 9. The requested numeric has scale {}"
52
+ )
53
+ NUMERIC_MAX_PRECISION_ERR_MSG = (
54
+ "Max precision for the whole component of a numeric is 29. The requested "
55
+ + "numeric has a whole component with precision {}"
56
+ )
57
+
58
+
59
+ if HAS_OPENTELEMETRY_INSTALLED:
60
+
61
+ class OpenTelemetryContextSetter(Setter):
62
+ """
63
+ Used by Open Telemetry for context propagation.
64
+ """
65
+
66
+ def set(self, carrier: List[Tuple[str, str]], key: str, value: str) -> None:
67
+ """
68
+ Injects trace context into Spanner metadata
69
+
70
+ Args:
71
+ carrier(PubsubMessage): The Pub/Sub message which is the carrier of Open Telemetry
72
+ data.
73
+ key(str): The key for which the Open Telemetry context data needs to be set.
74
+ value(str): The Open Telemetry context value to be set.
75
+
76
+ Returns:
77
+ None
78
+ """
79
+ carrier.append((key, value))
80
+
81
+
82
+ def _try_to_coerce_bytes(bytestring):
83
+ """Try to coerce a byte string into the right thing based on Python
84
+ version and whether or not it is base64 encoded.
85
+
86
+ Return a text string or raise ValueError.
87
+ """
88
+ # Attempt to coerce using google.protobuf.Value, which will expect
89
+ # something that is utf-8 (and base64 consistently is).
90
+ try:
91
+ Value(string_value=bytestring)
92
+ return bytestring
93
+ except ValueError:
94
+ raise ValueError(
95
+ "Received a bytes that is not base64 encoded. "
96
+ "Ensure that you either send a Unicode string or a "
97
+ "base64-encoded bytes."
98
+ )
99
+
100
+
101
+ def _merge_query_options(base, merge):
102
+ """Merge higher precedence QueryOptions with current QueryOptions.
103
+
104
+ :type base:
105
+ :class:`~google.cloud.spanner_v1.types.ExecuteSqlRequest.QueryOptions`
106
+ or :class:`dict` or None
107
+ :param base: The current QueryOptions that is intended for use.
108
+
109
+ :type merge:
110
+ :class:`~google.cloud.spanner_v1.types.ExecuteSqlRequest.QueryOptions`
111
+ or :class:`dict` or None
112
+ :param merge:
113
+ The QueryOptions that have a higher priority than base. These options
114
+ should overwrite the fields in base.
115
+
116
+ :rtype:
117
+ :class:`~google.cloud.spanner_v1.types.ExecuteSqlRequest.QueryOptions`
118
+ or None
119
+ :returns:
120
+ QueryOptions object formed by merging the two given QueryOptions.
121
+ If the resultant object only has empty fields, returns None.
122
+ """
123
+ combined = base or ExecuteSqlRequest.QueryOptions()
124
+ if type(combined) is dict:
125
+ combined = ExecuteSqlRequest.QueryOptions(
126
+ optimizer_version=combined.get("optimizer_version", ""),
127
+ optimizer_statistics_package=combined.get(
128
+ "optimizer_statistics_package", ""
129
+ ),
130
+ )
131
+ merge = merge or ExecuteSqlRequest.QueryOptions()
132
+ if type(merge) is dict:
133
+ merge = ExecuteSqlRequest.QueryOptions(
134
+ optimizer_version=merge.get("optimizer_version", ""),
135
+ optimizer_statistics_package=merge.get("optimizer_statistics_package", ""),
136
+ )
137
+ type(combined).pb(combined).MergeFrom(type(merge).pb(merge))
138
+ if not combined.optimizer_version and not combined.optimizer_statistics_package:
139
+ return None
140
+ return combined
141
+
142
+
143
+ def _assert_numeric_precision_and_scale(value):
144
+ """
145
+ Asserts that input numeric field is within Spanner supported range.
146
+
147
+ Spanner supports fixed 38 digits of precision and 9 digits of scale.
148
+ This number can be optionally prefixed with a plus or minus sign.
149
+ Read more here: https://cloud.google.com/spanner/docs/data-types#numeric_type
150
+
151
+ :type value: decimal.Decimal
152
+ :param value: The value to check for Cloud Spanner compatibility.
153
+
154
+ :raises NotSupportedError: If value is not within supported precision or scale of Spanner.
155
+ """
156
+ scale = value.as_tuple().exponent
157
+ precision = len(value.as_tuple().digits)
158
+
159
+ if scale < -9:
160
+ raise ValueError(NUMERIC_MAX_SCALE_ERR_MSG.format(abs(scale)))
161
+ if precision + scale > 29:
162
+ raise ValueError(NUMERIC_MAX_PRECISION_ERR_MSG.format(precision + scale))
163
+
164
+
165
+ def _datetime_to_rfc3339(value):
166
+ """Format the provided datatime in the RFC 3339 format.
167
+
168
+ :type value: datetime.datetime
169
+ :param value: value to format
170
+
171
+ :rtype: str
172
+ :returns: RFC 3339 formatted datetime string
173
+ """
174
+ # Convert to UTC and then drop the timezone so we can append "Z" in lieu of
175
+ # allowing isoformat to append the "+00:00" zone offset.
176
+ value = value.astimezone(datetime.timezone.utc).replace(tzinfo=None)
177
+ return value.isoformat(sep="T", timespec="microseconds") + "Z"
178
+
179
+
180
+ def _datetime_to_rfc3339_nanoseconds(value):
181
+ """Format the provided datatime in the RFC 3339 format.
182
+
183
+ :type value: datetime_helpers.DatetimeWithNanoseconds
184
+ :param value: value to format
185
+
186
+ :rtype: str
187
+ :returns: RFC 3339 formatted datetime string
188
+ """
189
+
190
+ if value.nanosecond == 0:
191
+ return _datetime_to_rfc3339(value)
192
+ nanos = str(value.nanosecond).rjust(9, "0").rstrip("0")
193
+ # Convert to UTC and then drop the timezone so we can append "Z" in lieu of
194
+ # allowing isoformat to append the "+00:00" zone offset.
195
+ value = value.astimezone(datetime.timezone.utc).replace(tzinfo=None)
196
+ return "{}.{}Z".format(value.isoformat(sep="T", timespec="seconds"), nanos)
197
+
198
+
199
+ def _make_value_pb(value):
200
+ """Helper for :func:`_make_list_value_pbs`.
201
+
202
+ :type value: scalar value
203
+ :param value: value to convert
204
+
205
+ :rtype: :class:`~google.protobuf.struct_pb2.Value`
206
+ :returns: value protobufs
207
+ :raises ValueError: if value is not of a known scalar type.
208
+ """
209
+ if value is None:
210
+ return Value(null_value="NULL_VALUE")
211
+ if isinstance(value, (list, tuple)):
212
+ return Value(list_value=_make_list_value_pb(value))
213
+ if isinstance(value, bool):
214
+ return Value(bool_value=value)
215
+ if isinstance(value, int):
216
+ return Value(string_value=str(value))
217
+ if isinstance(value, float):
218
+ if math.isnan(value):
219
+ return Value(string_value="NaN")
220
+ if math.isinf(value):
221
+ if value > 0:
222
+ return Value(string_value="Infinity")
223
+ else:
224
+ return Value(string_value="-Infinity")
225
+ return Value(number_value=value)
226
+ if isinstance(value, datetime_helpers.DatetimeWithNanoseconds):
227
+ return Value(string_value=_datetime_to_rfc3339_nanoseconds(value))
228
+ if isinstance(value, datetime.datetime):
229
+ return Value(string_value=_datetime_to_rfc3339(value))
230
+ if isinstance(value, datetime.date):
231
+ return Value(string_value=value.isoformat())
232
+ if isinstance(value, bytes):
233
+ value = _try_to_coerce_bytes(value)
234
+ return Value(string_value=value)
235
+ if isinstance(value, str):
236
+ return Value(string_value=value)
237
+ if isinstance(value, ListValue):
238
+ return Value(list_value=value)
239
+ if isinstance(value, decimal.Decimal):
240
+ _assert_numeric_precision_and_scale(value)
241
+ return Value(string_value=str(value))
242
+ if isinstance(value, JsonObject):
243
+ value = value.serialize()
244
+ if value is None:
245
+ return Value(null_value="NULL_VALUE")
246
+ else:
247
+ return Value(string_value=value)
248
+ if isinstance(value, Message):
249
+ value = value.SerializeToString()
250
+ if value is None:
251
+ return Value(null_value="NULL_VALUE")
252
+ else:
253
+ return Value(string_value=base64.b64encode(value))
254
+ if isinstance(value, Interval):
255
+ return Value(string_value=str(value))
256
+
257
+ raise ValueError("Unknown type: %s" % (value,))
258
+
259
+
260
+ def _make_list_value_pb(values):
261
+ """Construct of ListValue protobufs.
262
+
263
+ :type values: list of scalar
264
+ :param values: Row data
265
+
266
+ :rtype: :class:`~google.protobuf.struct_pb2.ListValue`
267
+ :returns: protobuf
268
+ """
269
+ return ListValue(values=[_make_value_pb(value) for value in values])
270
+
271
+
272
+ def _make_list_value_pbs(values):
273
+ """Construct a sequence of ListValue protobufs.
274
+
275
+ :type values: list of list of scalar
276
+ :param values: Row data
277
+
278
+ :rtype: list of :class:`~google.protobuf.struct_pb2.ListValue`
279
+ :returns: sequence of protobufs
280
+ """
281
+ return [_make_list_value_pb(row) for row in values]
282
+
283
+
284
+ def _parse_value_pb(value_pb, field_type, field_name, column_info=None):
285
+ """Convert a Value protobuf to cell data.
286
+
287
+ :type value_pb: :class:`~google.protobuf.struct_pb2.Value`
288
+ :param value_pb: protobuf to convert
289
+
290
+ :type field_type: :class:`~google.cloud.spanner_v1.types.Type`
291
+ :param field_type: type code for the value
292
+
293
+ :type field_name: str
294
+ :param field_name: column name
295
+
296
+ :type column_info: dict
297
+ :param column_info: (Optional) dict of column name and column information.
298
+ An object where column names as keys and custom objects as corresponding
299
+ values for deserialization. It's specifically useful for data types like
300
+ protobuf where deserialization logic is on user-specific code. When provided,
301
+ the custom object enables deserialization of backend-received column data.
302
+ If not provided, data remains serialized as bytes for Proto Messages and
303
+ integer for Proto Enums.
304
+
305
+ :rtype: varies on field_type
306
+ :returns: value extracted from value_pb
307
+ :raises ValueError: if unknown type is passed
308
+ """
309
+ decoder = _get_type_decoder(field_type, field_name, column_info)
310
+ return _parse_nullable(value_pb, decoder)
311
+
312
+
313
+ def _get_type_decoder(field_type, field_name, column_info=None):
314
+ """Returns a function that converts a Value protobuf to cell data.
315
+
316
+ :type field_type: :class:`~google.cloud.spanner_v1.types.Type`
317
+ :param field_type: type code for the value
318
+
319
+ :type field_name: str
320
+ :param field_name: column name
321
+
322
+ :type column_info: dict
323
+ :param column_info: (Optional) dict of column name and column information.
324
+ An object where column names as keys and custom objects as corresponding
325
+ values for deserialization. It's specifically useful for data types like
326
+ protobuf where deserialization logic is on user-specific code. When provided,
327
+ the custom object enables deserialization of backend-received column data.
328
+ If not provided, data remains serialized as bytes for Proto Messages and
329
+ integer for Proto Enums.
330
+
331
+ :rtype: a function that takes a single protobuf value as an input argument
332
+ :returns: a function that can be used to extract a value from a protobuf value
333
+ :raises ValueError: if unknown type is passed
334
+ """
335
+
336
+ type_code = field_type.code
337
+ if type_code == TypeCode.STRING:
338
+ return _parse_string
339
+ elif type_code == TypeCode.BYTES:
340
+ return _parse_bytes
341
+ elif type_code == TypeCode.BOOL:
342
+ return _parse_bool
343
+ elif type_code == TypeCode.INT64:
344
+ return _parse_int64
345
+ elif type_code == TypeCode.FLOAT64:
346
+ return _parse_float
347
+ elif type_code == TypeCode.FLOAT32:
348
+ return _parse_float
349
+ elif type_code == TypeCode.DATE:
350
+ return _parse_date
351
+ elif type_code == TypeCode.TIMESTAMP:
352
+ return _parse_timestamp
353
+ elif type_code == TypeCode.NUMERIC:
354
+ return _parse_numeric
355
+ elif type_code == TypeCode.JSON:
356
+ return _parse_json
357
+ elif type_code == TypeCode.PROTO:
358
+ return lambda value_pb: _parse_proto(value_pb, column_info, field_name)
359
+ elif type_code == TypeCode.ENUM:
360
+ return lambda value_pb: _parse_proto_enum(value_pb, column_info, field_name)
361
+ elif type_code == TypeCode.ARRAY:
362
+ element_decoder = _get_type_decoder(
363
+ field_type.array_element_type, field_name, column_info
364
+ )
365
+ return lambda value_pb: _parse_array(value_pb, element_decoder)
366
+ elif type_code == TypeCode.STRUCT:
367
+ element_decoders = [
368
+ _get_type_decoder(item_field.type_, field_name, column_info)
369
+ for item_field in field_type.struct_type.fields
370
+ ]
371
+ return lambda value_pb: _parse_struct(value_pb, element_decoders)
372
+ elif type_code == TypeCode.INTERVAL:
373
+ return _parse_interval
374
+ else:
375
+ raise ValueError("Unknown type: %s" % (field_type,))
376
+
377
+
378
+ def _parse_list_value_pbs(rows, row_type):
379
+ """Convert a list of ListValue protobufs into a list of list of cell data.
380
+
381
+ :type rows: list of :class:`~google.protobuf.struct_pb2.ListValue`
382
+ :param rows: row data returned from a read/query
383
+
384
+ :type row_type: :class:`~google.cloud.spanner_v1.types.StructType`
385
+ :param row_type: row schema specification
386
+
387
+ :rtype: list of list of cell data
388
+ :returns: data for the rows, coerced into appropriate types
389
+ """
390
+ result = []
391
+ for row in rows:
392
+ row_data = []
393
+ for value_pb, field in zip(row.values, row_type.fields):
394
+ row_data.append(_parse_value_pb(value_pb, field.type_, field.name))
395
+ result.append(row_data)
396
+ return result
397
+
398
+
399
+ def _parse_string(value_pb) -> str:
400
+ return value_pb.string_value
401
+
402
+
403
+ def _parse_bytes(value_pb):
404
+ return value_pb.string_value.encode("utf8")
405
+
406
+
407
+ def _parse_bool(value_pb) -> bool:
408
+ return value_pb.bool_value
409
+
410
+
411
+ def _parse_int64(value_pb) -> int:
412
+ return int(value_pb.string_value)
413
+
414
+
415
+ def _parse_float(value_pb) -> float:
416
+ if value_pb.HasField("string_value"):
417
+ return float(value_pb.string_value)
418
+ else:
419
+ return value_pb.number_value
420
+
421
+
422
+ def _parse_date(value_pb):
423
+ return _date_from_iso8601_date(value_pb.string_value)
424
+
425
+
426
+ def _parse_timestamp(value_pb):
427
+ DatetimeWithNanoseconds = datetime_helpers.DatetimeWithNanoseconds
428
+ return DatetimeWithNanoseconds.from_rfc3339(value_pb.string_value)
429
+
430
+
431
+ def _parse_numeric(value_pb):
432
+ return decimal.Decimal(value_pb.string_value)
433
+
434
+
435
+ def _parse_json(value_pb):
436
+ return JsonObject.from_str(value_pb.string_value)
437
+
438
+
439
+ def _parse_proto(value_pb, column_info, field_name):
440
+ bytes_value = base64.b64decode(value_pb.string_value)
441
+ if column_info is not None and column_info.get(field_name) is not None:
442
+ default_proto_message = column_info.get(field_name)
443
+ if isinstance(default_proto_message, Message):
444
+ proto_message = type(default_proto_message)()
445
+ proto_message.ParseFromString(bytes_value)
446
+ return proto_message
447
+ return bytes_value
448
+
449
+
450
+ def _parse_proto_enum(value_pb, column_info, field_name):
451
+ int_value = int(value_pb.string_value)
452
+ if column_info is not None and column_info.get(field_name) is not None:
453
+ proto_enum = column_info.get(field_name)
454
+ if isinstance(proto_enum, EnumTypeWrapper):
455
+ return proto_enum.Name(int_value)
456
+ return int_value
457
+
458
+
459
+ def _parse_array(value_pb, element_decoder) -> []:
460
+ return [
461
+ _parse_nullable(item_pb, element_decoder)
462
+ for item_pb in value_pb.list_value.values
463
+ ]
464
+
465
+
466
+ def _parse_struct(value_pb, element_decoders):
467
+ return [
468
+ _parse_nullable(item_pb, element_decoders[i])
469
+ for (i, item_pb) in enumerate(value_pb.list_value.values)
470
+ ]
471
+
472
+
473
+ def _parse_nullable(value_pb, decoder):
474
+ if value_pb.HasField("null_value"):
475
+ return None
476
+ else:
477
+ return decoder(value_pb)
478
+
479
+
480
+ def _parse_interval(value_pb):
481
+ """Parse a Value protobuf containing an interval."""
482
+ if hasattr(value_pb, "string_value"):
483
+ return Interval.from_str(value_pb.string_value)
484
+ return Interval.from_str(value_pb)
485
+
486
+
487
+ class _SessionWrapper(object):
488
+ """Base class for objects wrapping a session.
489
+
490
+ :type session: :class:`~google.cloud.spanner_v1.session.Session`
491
+ :param session: the session used to perform the commit
492
+ """
493
+
494
+ def __init__(self, session):
495
+ self._session = session
496
+
497
+
498
+ def _metadata_with_prefix(prefix, **kw):
499
+ """Create RPC metadata containing a prefix.
500
+
501
+ Args:
502
+ prefix (str): appropriate resource path.
503
+
504
+ Returns:
505
+ List[Tuple[str, str]]: RPC metadata with supplied prefix
506
+ """
507
+ return [("google-cloud-resource-prefix", prefix)]
508
+
509
+
510
+ def _retry_on_aborted_exception(
511
+ func,
512
+ deadline,
513
+ default_retry_delay=None,
514
+ ):
515
+ """
516
+ Handles retry logic for Aborted exceptions, considering the deadline.
517
+ """
518
+ attempts = 0
519
+ while True:
520
+ try:
521
+ attempts += 1
522
+ return func()
523
+ except Aborted as exc:
524
+ _delay_until_retry(
525
+ exc,
526
+ deadline=deadline,
527
+ attempts=attempts,
528
+ default_retry_delay=default_retry_delay,
529
+ )
530
+ continue
531
+
532
+
533
+ def _retry(
534
+ func,
535
+ retry_count=5,
536
+ delay=2,
537
+ allowed_exceptions=None,
538
+ beforeNextRetry=None,
539
+ ):
540
+ """
541
+ Retry a function with a specified number of retries, delay between retries, and list of allowed exceptions.
542
+
543
+ Args:
544
+ func: The function to be retried.
545
+ retry_count: The maximum number of times to retry the function.
546
+ delay: The delay in seconds between retries.
547
+ allowed_exceptions: A tuple of exceptions that are allowed to occur without triggering a retry.
548
+ Passing allowed_exceptions as None will lead to retrying for all exceptions.
549
+
550
+ Returns:
551
+ The result of the function if it is successful, or raises the last exception if all retries fail.
552
+ """
553
+ retries = 0
554
+ while retries <= retry_count:
555
+ if retries > 0 and beforeNextRetry:
556
+ beforeNextRetry(retries, delay)
557
+
558
+ try:
559
+ return func()
560
+ except Exception as exc:
561
+ if (
562
+ allowed_exceptions is None or exc.__class__ in allowed_exceptions
563
+ ) and retries < retry_count:
564
+ if (
565
+ allowed_exceptions is not None
566
+ and allowed_exceptions[exc.__class__] is not None
567
+ ):
568
+ allowed_exceptions[exc.__class__](exc)
569
+ time.sleep(delay)
570
+ delay = delay * 2
571
+ retries = retries + 1
572
+ else:
573
+ raise exc
574
+
575
+
576
+ def _check_rst_stream_error(exc):
577
+ resumable_error = (
578
+ any(
579
+ resumable_message in exc.message
580
+ for resumable_message in (
581
+ "RST_STREAM",
582
+ "Received unexpected EOS on DATA frame from server",
583
+ )
584
+ ),
585
+ )
586
+ if not resumable_error:
587
+ raise
588
+
589
+
590
+ def _metadata_with_leader_aware_routing(value, **kw):
591
+ """Create RPC metadata containing a leader aware routing header
592
+
593
+ Args:
594
+ value (bool): header value
595
+
596
+ Returns:
597
+ List[Tuple[str, str]]: RPC metadata with leader aware routing header
598
+ """
599
+ return ("x-goog-spanner-route-to-leader", str(value).lower())
600
+
601
+
602
+ def _metadata_with_span_context(metadata: List[Tuple[str, str]], **kw) -> None:
603
+ """
604
+ Appends metadata with end to end tracing header and OpenTelemetry span context .
605
+
606
+ Args:
607
+ metadata (list[tuple[str, str]]): The metadata carrier where the OpenTelemetry context
608
+ should be injected.
609
+ Returns:
610
+ None
611
+ """
612
+ if HAS_OPENTELEMETRY_INSTALLED and metadata is not None:
613
+ metadata.append(("x-goog-spanner-end-to-end-tracing", "true"))
614
+ inject(setter=OpenTelemetryContextSetter(), carrier=metadata)
615
+
616
+
617
+ def _delay_until_retry(exc, deadline, attempts, default_retry_delay=None):
618
+ """Helper for :meth:`Session.run_in_transaction`.
619
+
620
+ Detect retryable abort, and impose server-supplied delay.
621
+
622
+ :type exc: :class:`google.api_core.exceptions.Aborted`
623
+ :param exc: exception for aborted transaction
624
+
625
+ :type deadline: float
626
+ :param deadline: maximum timestamp to continue retrying the transaction.
627
+
628
+ :type attempts: int
629
+ :param attempts: number of call retries
630
+ """
631
+
632
+ cause = exc.errors[0]
633
+ now = time.time()
634
+ if now >= deadline:
635
+ raise
636
+
637
+ delay = _get_retry_delay(cause, attempts, default_retry_delay=default_retry_delay)
638
+ if delay is not None:
639
+ if now + delay > deadline:
640
+ raise
641
+
642
+ time.sleep(delay)
643
+
644
+
645
+ def _get_retry_delay(cause, attempts, default_retry_delay=None):
646
+ """Helper for :func:`_delay_until_retry`.
647
+
648
+ :type exc: :class:`grpc.Call`
649
+ :param exc: exception for aborted transaction
650
+
651
+ :rtype: float
652
+ :returns: seconds to wait before retrying the transaction.
653
+
654
+ :type attempts: int
655
+ :param attempts: number of call retries
656
+ """
657
+ if hasattr(cause, "trailing_metadata"):
658
+ metadata = dict(cause.trailing_metadata())
659
+ else:
660
+ metadata = {}
661
+ retry_info_pb = metadata.get("google.rpc.retryinfo-bin")
662
+ if retry_info_pb is not None:
663
+ retry_info = RetryInfo()
664
+ retry_info.ParseFromString(retry_info_pb)
665
+ nanos = retry_info.retry_delay.nanos
666
+ return retry_info.retry_delay.seconds + nanos / 1.0e9
667
+ if default_retry_delay is not None:
668
+ return default_retry_delay
669
+
670
+ return 2**attempts + random.random()
671
+
672
+
673
+ class AtomicCounter:
674
+ def __init__(self, start_value=0):
675
+ self.__lock = threading.Lock()
676
+ self.__value = start_value
677
+
678
+ @property
679
+ def value(self):
680
+ with self.__lock:
681
+ return self.__value
682
+
683
+ def increment(self, n=1):
684
+ with self.__lock:
685
+ self.__value += n
686
+ return self.__value
687
+
688
+ def __iadd__(self, n):
689
+ """
690
+ Defines the inplace += operator result.
691
+ """
692
+ with self.__lock:
693
+ self.__value += n
694
+ return self
695
+
696
+ def __add__(self, n):
697
+ """
698
+ Defines the result of invoking: value = AtomicCounter + addable
699
+ """
700
+ with self.__lock:
701
+ n += self.__value
702
+ return n
703
+
704
+ def __radd__(self, n):
705
+ """
706
+ Defines the result of invoking: value = addable + AtomicCounter
707
+ """
708
+ return self.__add__(n)
709
+
710
+ def reset(self):
711
+ with self.__lock:
712
+ self.__value = 0
713
+
714
+
715
+ def _metadata_with_request_id(*args, **kwargs):
716
+ return with_request_id(*args, **kwargs)
717
+
718
+
719
+ def _merge_Transaction_Options(
720
+ defaultTransactionOptions: TransactionOptions,
721
+ mergeTransactionOptions: TransactionOptions,
722
+ ) -> TransactionOptions:
723
+ """Merges two TransactionOptions objects.
724
+
725
+ - Values from `mergeTransactionOptions` take precedence if set.
726
+ - Values from `defaultTransactionOptions` are used only if missing.
727
+
728
+ Args:
729
+ defaultTransactionOptions (TransactionOptions): The default transaction options (fallback values).
730
+ mergeTransactionOptions (TransactionOptions): The main transaction options (overrides when set).
731
+
732
+ Returns:
733
+ TransactionOptions: A merged TransactionOptions object.
734
+ """
735
+
736
+ if defaultTransactionOptions is None:
737
+ return mergeTransactionOptions
738
+
739
+ if mergeTransactionOptions is None:
740
+ return defaultTransactionOptions
741
+
742
+ merged_pb = TransactionOptions()._pb # Create a new protobuf object
743
+
744
+ # Merge defaultTransactionOptions first
745
+ merged_pb.MergeFrom(defaultTransactionOptions._pb)
746
+
747
+ # Merge transactionOptions, ensuring it overrides default values
748
+ merged_pb.MergeFrom(mergeTransactionOptions._pb)
749
+
750
+ # Convert protobuf object back into a TransactionOptions instance
751
+ return TransactionOptions(merged_pb)