|
14 | 14 |
|
15 | 15 | """User friendly container for Cloud Spanner Database."""
|
16 | 16 |
|
| 17 | +import copy |
| 18 | +import functools |
17 | 19 | import re
|
18 | 20 | import threading
|
19 |
| -import copy |
20 | 21 |
|
21 | 22 | from google.api_core.gapic_v1 import client_info
|
22 | 23 | import google.auth.credentials
|
| 24 | +from google.protobuf.struct_pb2 import Struct |
23 | 25 | from google.cloud.exceptions import NotFound
|
24 | 26 | import six
|
25 | 27 |
|
26 | 28 | # pylint: disable=ungrouped-imports
|
27 | 29 | from google.cloud.spanner_v1 import __version__
|
| 30 | +from google.cloud.spanner_v1._helpers import _make_value_pb |
28 | 31 | from google.cloud.spanner_v1._helpers import _metadata_with_prefix
|
29 | 32 | from google.cloud.spanner_v1.batch import Batch
|
30 | 33 | from google.cloud.spanner_v1.gapic.spanner_client import SpannerClient
|
31 | 34 | from google.cloud.spanner_v1.keyset import KeySet
|
32 | 35 | from google.cloud.spanner_v1.pool import BurstyPool
|
33 | 36 | from google.cloud.spanner_v1.pool import SessionCheckout
|
34 | 37 | from google.cloud.spanner_v1.session import Session
|
| 38 | +from google.cloud.spanner_v1.snapshot import _restart_on_unavailable |
35 | 39 | from google.cloud.spanner_v1.snapshot import Snapshot
|
| 40 | +from google.cloud.spanner_v1.streamed import StreamedResultSet |
| 41 | +from google.cloud.spanner_v1.proto.transaction_pb2 import ( |
| 42 | + TransactionSelector, TransactionOptions) |
36 | 43 | # pylint: enable=ungrouped-imports
|
37 | 44 |
|
38 | 45 |
|
@@ -272,6 +279,70 @@ def drop(self):
|
272 | 279 | metadata = _metadata_with_prefix(self.name)
|
273 | 280 | api.drop_database(self.name, metadata=metadata)
|
274 | 281 |
|
| 282 | + def execute_partitioned_dml( |
| 283 | + self, dml, params=None, param_types=None, query_mode=None): |
| 284 | + """Execute a partitionable DML statement. |
| 285 | +
|
| 286 | + :type dml: str |
| 287 | + :param dml: SQL DML statement |
| 288 | +
|
| 289 | + :type params: dict, {str -> column value} |
| 290 | + :param params: values for parameter replacement. Keys must match |
| 291 | + the names used in ``dml``. |
| 292 | +
|
| 293 | + :type param_types: dict[str -> Union[dict, .types.Type]] |
| 294 | + :param param_types: |
| 295 | + (Optional) maps explicit types for one or more param values; |
| 296 | + required if parameters are passed. |
| 297 | +
|
| 298 | + :type query_mode: |
| 299 | + :class:`google.cloud.spanner_v1.proto.ExecuteSqlRequest.QueryMode` |
| 300 | + :param query_mode: Mode governing return of results / query plan. See |
| 301 | + https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.ExecuteSqlRequest.QueryMode1 |
| 302 | +
|
| 303 | + :rtype: int |
| 304 | + :returns: Count of rows affected by the DML statement. |
| 305 | + """ |
| 306 | + if params is not None: |
| 307 | + if param_types is None: |
| 308 | + raise ValueError( |
| 309 | + "Specify 'param_types' when passing 'params'.") |
| 310 | + params_pb = Struct(fields={ |
| 311 | + key: _make_value_pb(value) for key, value in params.items()}) |
| 312 | + else: |
| 313 | + params_pb = None |
| 314 | + |
| 315 | + api = self.spanner_api |
| 316 | + |
| 317 | + txn_options = TransactionOptions( |
| 318 | + partitioned_dml=TransactionOptions.PartitionedDml()) |
| 319 | + |
| 320 | + metadata = _metadata_with_prefix(self.name) |
| 321 | + |
| 322 | + with SessionCheckout(self._pool) as session: |
| 323 | + |
| 324 | + txn = api.begin_transaction( |
| 325 | + session.name, txn_options, metadata=metadata) |
| 326 | + |
| 327 | + txn_selector = TransactionSelector(id=txn.id) |
| 328 | + |
| 329 | + restart = functools.partial( |
| 330 | + api.execute_streaming_sql, |
| 331 | + session.name, |
| 332 | + dml, |
| 333 | + transaction=txn_selector, |
| 334 | + params=params_pb, |
| 335 | + param_types=param_types, |
| 336 | + query_mode=query_mode, |
| 337 | + metadata=metadata) |
| 338 | + |
| 339 | + iterator = _restart_on_unavailable(restart) |
| 340 | + |
| 341 | + result_set = StreamedResultSet(iterator) |
| 342 | + list(result_set) # consume all partials |
| 343 | + |
| 344 | + return result_set.stats.row_count_lower_bound |
| 345 | + |
275 | 346 | def session(self, labels=None):
|
276 | 347 | """Factory to create a session for this database.
|
277 | 348 |
|
|
0 commit comments