google-cloud-spanner 3.58.0__tar.gz → 3.59.0__tar.gz

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 (211) hide show
  1. {google_cloud_spanner-3.58.0/google_cloud_spanner.egg-info → google_cloud_spanner-3.59.0}/PKG-INFO +1 -1
  2. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/gapic_version.py +1 -1
  3. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/gapic_version.py +1 -1
  4. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/client.py +20 -11
  5. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/database.py +28 -1
  6. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/gapic_version.py +1 -1
  7. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/merged_result_set.py +37 -4
  8. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/metrics/spanner_metrics_tracer_factory.py +18 -10
  9. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0/google_cloud_spanner.egg-info}/PKG-INFO +1 -1
  10. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google_cloud_spanner.egg-info/SOURCES.txt +1 -0
  11. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_client.py +23 -0
  12. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_database.py +15 -0
  13. google_cloud_spanner-3.59.0/tests/unit/test_merged_result_set.py +119 -0
  14. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_spanner_metrics_tracer_factory.py +47 -0
  15. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/LICENSE +0 -0
  16. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/MANIFEST.in +0 -0
  17. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/README.rst +0 -0
  18. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner.py +0 -0
  19. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/__init__.py +0 -0
  20. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/gapic_metadata.json +0 -0
  21. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/py.typed +0 -0
  22. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/__init__.py +0 -0
  23. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/__init__.py +0 -0
  24. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py +0 -0
  25. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/client.py +0 -0
  26. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/pagers.py +0 -0
  27. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/__init__.py +0 -0
  28. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py +0 -0
  29. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py +0 -0
  30. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py +0 -0
  31. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py +0 -0
  32. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py +0 -0
  33. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/types/__init__.py +0 -0
  34. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/types/backup.py +0 -0
  35. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/types/backup_schedule.py +0 -0
  36. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/types/common.py +0 -0
  37. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py +0 -0
  38. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/__init__.py +0 -0
  39. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/gapic_metadata.json +0 -0
  40. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/py.typed +0 -0
  41. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/__init__.py +0 -0
  42. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/__init__.py +0 -0
  43. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py +0 -0
  44. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py +0 -0
  45. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/pagers.py +0 -0
  46. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/__init__.py +0 -0
  47. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py +0 -0
  48. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py +0 -0
  49. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py +0 -0
  50. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py +0 -0
  51. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py +0 -0
  52. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/types/__init__.py +0 -0
  53. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/types/common.py +0 -0
  54. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_admin_instance_v1/types/spanner_instance_admin.py +0 -0
  55. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/__init__.py +0 -0
  56. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/_helpers.py +0 -0
  57. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/batch_dml_executor.py +0 -0
  58. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/checksum.py +0 -0
  59. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/client_side_statement_executor.py +0 -0
  60. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/client_side_statement_parser.py +0 -0
  61. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/connection.py +0 -0
  62. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/cursor.py +0 -0
  63. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/exceptions.py +0 -0
  64. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/parse_utils.py +0 -0
  65. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/parsed_statement.py +0 -0
  66. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/parser.py +0 -0
  67. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/partition_helper.py +0 -0
  68. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/transaction_helper.py +0 -0
  69. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/types.py +0 -0
  70. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/utils.py +0 -0
  71. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_dbapi/version.py +0 -0
  72. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/__init__.py +0 -0
  73. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/_helpers.py +0 -0
  74. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/_opentelemetry_tracing.py +0 -0
  75. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/backup.py +0 -0
  76. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/batch.py +0 -0
  77. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/data_types.py +0 -0
  78. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/database_sessions_manager.py +0 -0
  79. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/gapic_metadata.json +0 -0
  80. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/instance.py +0 -0
  81. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/keyset.py +0 -0
  82. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/metrics/constants.py +0 -0
  83. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/metrics/metrics_capture.py +0 -0
  84. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/metrics/metrics_exporter.py +0 -0
  85. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/metrics/metrics_interceptor.py +0 -0
  86. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/metrics/metrics_tracer.py +0 -0
  87. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/metrics/metrics_tracer_factory.py +0 -0
  88. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/param_types.py +0 -0
  89. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/pool.py +0 -0
  90. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/py.typed +0 -0
  91. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/request_id_header.py +0 -0
  92. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/__init__.py +0 -0
  93. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/__init__.py +0 -0
  94. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/async_client.py +0 -0
  95. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/client.py +0 -0
  96. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/pagers.py +0 -0
  97. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/transports/__init__.py +0 -0
  98. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/transports/base.py +0 -0
  99. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/transports/grpc.py +0 -0
  100. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/transports/grpc_asyncio.py +0 -0
  101. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/transports/rest.py +0 -0
  102. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/services/spanner/transports/rest_base.py +0 -0
  103. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/session.py +0 -0
  104. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/snapshot.py +0 -0
  105. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/streamed.py +0 -0
  106. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/table.py +0 -0
  107. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/testing/__init__.py +0 -0
  108. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/testing/database_test.py +0 -0
  109. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/testing/interceptors.py +0 -0
  110. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/testing/mock_database_admin.py +0 -0
  111. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/testing/mock_spanner.py +0 -0
  112. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/testing/spanner_database_admin_pb2_grpc.py +0 -0
  113. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/testing/spanner_pb2_grpc.py +0 -0
  114. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/transaction.py +0 -0
  115. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/__init__.py +0 -0
  116. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/change_stream.py +0 -0
  117. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/commit_response.py +0 -0
  118. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/keys.py +0 -0
  119. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/mutation.py +0 -0
  120. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/query_plan.py +0 -0
  121. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/result_set.py +0 -0
  122. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/spanner.py +0 -0
  123. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/transaction.py +0 -0
  124. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google/cloud/spanner_v1/types/type.py +0 -0
  125. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google_cloud_spanner.egg-info/dependency_links.txt +0 -0
  126. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google_cloud_spanner.egg-info/not-zip-safe +0 -0
  127. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google_cloud_spanner.egg-info/requires.txt +0 -0
  128. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/google_cloud_spanner.egg-info/top_level.txt +0 -0
  129. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/setup.cfg +0 -0
  130. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/setup.py +0 -0
  131. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/__init__.py +0 -0
  132. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/_builders.py +0 -0
  133. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/_fixtures.py +0 -0
  134. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/_helpers.py +0 -0
  135. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/mockserver_tests/__init__.py +0 -0
  136. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/mockserver_tests/mock_server_test_base.py +0 -0
  137. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/mockserver_tests/test_aborted_transaction.py +0 -0
  138. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/mockserver_tests/test_basics.py +0 -0
  139. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/mockserver_tests/test_dbapi_autocommit.py +0 -0
  140. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/mockserver_tests/test_dbapi_isolation_level.py +0 -0
  141. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/mockserver_tests/test_request_id_header.py +0 -0
  142. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/mockserver_tests/test_tags.py +0 -0
  143. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/__init__.py +0 -0
  144. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/_helpers.py +0 -0
  145. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/_sample_data.py +0 -0
  146. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/conftest.py +0 -0
  147. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/test_backup_api.py +0 -0
  148. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/test_database_api.py +0 -0
  149. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/test_dbapi.py +0 -0
  150. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/test_instance_api.py +0 -0
  151. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/test_observability_options.py +0 -0
  152. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/test_session_api.py +0 -0
  153. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/test_streaming_chunking.py +0 -0
  154. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/test_table_api.py +0 -0
  155. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/testdata/descriptors.pb +0 -0
  156. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/testdata/singer.proto +0 -0
  157. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/testdata/singer_pb2.py +0 -0
  158. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/utils/__init__.py +0 -0
  159. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/utils/clear_streaming.py +0 -0
  160. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/utils/populate_streaming.py +0 -0
  161. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/utils/scrub_instances.py +0 -0
  162. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/system/utils/streaming_utils.py +0 -0
  163. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/__init__.py +0 -0
  164. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/gapic/__init__.py +0 -0
  165. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/gapic/spanner_admin_database_v1/__init__.py +0 -0
  166. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/gapic/spanner_admin_database_v1/test_database_admin.py +0 -0
  167. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/gapic/spanner_admin_instance_v1/__init__.py +0 -0
  168. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/gapic/spanner_admin_instance_v1/test_instance_admin.py +0 -0
  169. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/gapic/spanner_v1/__init__.py +0 -0
  170. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/gapic/spanner_v1/test_spanner.py +0 -0
  171. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/__init__.py +0 -0
  172. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test__helpers.py +0 -0
  173. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_batch_dml_executor.py +0 -0
  174. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_checksum.py +0 -0
  175. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_client_side_statement_executor.py +0 -0
  176. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_connect.py +0 -0
  177. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_connection.py +0 -0
  178. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_cursor.py +0 -0
  179. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_globals.py +0 -0
  180. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_parse_utils.py +0 -0
  181. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_parser.py +0 -0
  182. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_transaction_helper.py +0 -0
  183. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_types.py +0 -0
  184. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/spanner_dbapi/test_utils.py +0 -0
  185. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/streaming-read-acceptance-test.json +0 -0
  186. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test__helpers.py +0 -0
  187. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test__opentelemetry_tracing.py +0 -0
  188. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_atomic_counter.py +0 -0
  189. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_backup.py +0 -0
  190. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_batch.py +0 -0
  191. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_database_session_manager.py +0 -0
  192. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_datatypes.py +0 -0
  193. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_instance.py +0 -0
  194. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_keyset.py +0 -0
  195. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_metrics.py +0 -0
  196. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_metrics_capture.py +0 -0
  197. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_metrics_exporter.py +0 -0
  198. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_metrics_interceptor.py +0 -0
  199. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_metrics_tracer.py +0 -0
  200. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_metrics_tracer_factory.py +0 -0
  201. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_packaging.py +0 -0
  202. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_param_types.py +0 -0
  203. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_pool.py +0 -0
  204. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_session.py +0 -0
  205. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_snapshot.py +0 -0
  206. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_spanner.py +0 -0
  207. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_streamed.py +0 -0
  208. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_table.py +0 -0
  209. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/test_transaction.py +0 -0
  210. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/testdata/singer.proto +0 -0
  211. {google_cloud_spanner-3.58.0 → google_cloud_spanner-3.59.0}/tests/unit/testdata/singer_pb2.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-cloud-spanner
3
- Version: 3.58.0
3
+ Version: 3.59.0
4
4
  Summary: Google Cloud Spanner API client library
5
5
  Home-page: https://github.com/googleapis/python-spanner
6
6
  Author: Google LLC
@@ -13,4 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- __version__ = "3.58.0" # {x-release-please-version}
16
+ __version__ = "3.59.0" # {x-release-please-version}
@@ -13,4 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- __version__ = "3.58.0" # {x-release-please-version}
16
+ __version__ = "3.59.0" # {x-release-please-version}
@@ -25,6 +25,7 @@ In the hierarchy of API concepts
25
25
  """
26
26
  import grpc
27
27
  import os
28
+ import logging
28
29
  import warnings
29
30
 
30
31
  from google.api_core.gapic_v1 import client_info
@@ -97,6 +98,9 @@ def _get_spanner_optimizer_statistics_package():
97
98
  return os.getenv(OPTIMIZER_STATISITCS_PACKAGE_ENV_VAR, "")
98
99
 
99
100
 
101
+ log = logging.getLogger(__name__)
102
+
103
+
100
104
  def _get_spanner_enable_builtin_metrics():
101
105
  return os.getenv(ENABLE_SPANNER_METRICS_ENV_VAR) == "true"
102
106
 
@@ -240,19 +244,24 @@ class Client(ClientWithProject):
240
244
  and HAS_GOOGLE_CLOUD_MONITORING_INSTALLED
241
245
  ):
242
246
  meter_provider = metrics.NoOpMeterProvider()
243
- if not _get_spanner_emulator_host():
244
- meter_provider = MeterProvider(
245
- metric_readers=[
246
- PeriodicExportingMetricReader(
247
- CloudMonitoringMetricsExporter(
248
- project_id=project, credentials=credentials
247
+ try:
248
+ if not _get_spanner_emulator_host():
249
+ meter_provider = MeterProvider(
250
+ metric_readers=[
251
+ PeriodicExportingMetricReader(
252
+ CloudMonitoringMetricsExporter(
253
+ project_id=project, credentials=credentials
254
+ ),
255
+ export_interval_millis=METRIC_EXPORT_INTERVAL_MS,
249
256
  ),
250
- export_interval_millis=METRIC_EXPORT_INTERVAL_MS,
251
- )
252
- ]
257
+ ]
258
+ )
259
+ metrics.set_meter_provider(meter_provider)
260
+ SpannerMetricsTracerFactory()
261
+ except Exception as e:
262
+ log.warning(
263
+ "Failed to initialize Spanner built-in metrics. Error: %s", e
253
264
  )
254
- metrics.set_meter_provider(meter_provider)
255
- SpannerMetricsTracerFactory()
256
265
  else:
257
266
  SpannerMetricsTracerFactory(enabled=False)
258
267
 
@@ -1532,6 +1532,14 @@ class BatchSnapshot(object):
1532
1532
  "transaction_id": snapshot._transaction_id,
1533
1533
  }
1534
1534
 
1535
+ def __enter__(self):
1536
+ """Begin ``with`` block."""
1537
+ return self
1538
+
1539
+ def __exit__(self, exc_type, exc_val, exc_tb):
1540
+ """End ``with`` block."""
1541
+ self.close()
1542
+
1535
1543
  @property
1536
1544
  def observability_options(self):
1537
1545
  return getattr(self._database, "observability_options", {})
@@ -1703,6 +1711,7 @@ class BatchSnapshot(object):
1703
1711
  *,
1704
1712
  retry=gapic_v1.method.DEFAULT,
1705
1713
  timeout=gapic_v1.method.DEFAULT,
1714
+ lazy_decode=False,
1706
1715
  ):
1707
1716
  """Process a single, partitioned read.
1708
1717
 
@@ -1717,6 +1726,14 @@ class BatchSnapshot(object):
1717
1726
  :type timeout: float
1718
1727
  :param timeout: (Optional) The timeout for this request.
1719
1728
 
1729
+ :type lazy_decode: bool
1730
+ :param lazy_decode:
1731
+ (Optional) If this argument is set to ``true``, the iterator
1732
+ returns the underlying protobuf values instead of decoded Python
1733
+ objects. This reduces the time that is needed to iterate through
1734
+ large result sets. The application is responsible for decoding
1735
+ the data that is needed.
1736
+
1720
1737
 
1721
1738
  :rtype: :class:`~google.cloud.spanner_v1.streamed.StreamedResultSet`
1722
1739
  :returns: a result set instance which can be used to consume rows.
@@ -1844,6 +1861,7 @@ class BatchSnapshot(object):
1844
1861
  self,
1845
1862
  batch,
1846
1863
  *,
1864
+ lazy_decode: bool = False,
1847
1865
  retry=gapic_v1.method.DEFAULT,
1848
1866
  timeout=gapic_v1.method.DEFAULT,
1849
1867
  ):
@@ -1854,6 +1872,13 @@ class BatchSnapshot(object):
1854
1872
  one of the mappings returned from an earlier call to
1855
1873
  :meth:`generate_query_batches`.
1856
1874
 
1875
+ :type lazy_decode: bool
1876
+ :param lazy_decode:
1877
+ (Optional) If this argument is set to ``true``, the iterator
1878
+ returns the underlying protobuf values instead of decoded Python
1879
+ objects. This reduces the time that is needed to iterate through
1880
+ large result sets.
1881
+
1857
1882
  :type retry: :class:`~google.api_core.retry.Retry`
1858
1883
  :param retry: (Optional) The retry settings for this request.
1859
1884
 
@@ -1870,6 +1895,7 @@ class BatchSnapshot(object):
1870
1895
  return self._get_snapshot().execute_sql(
1871
1896
  partition=batch["partition"],
1872
1897
  **batch["query"],
1898
+ lazy_decode=lazy_decode,
1873
1899
  retry=retry,
1874
1900
  timeout=timeout,
1875
1901
  )
@@ -1883,6 +1909,7 @@ class BatchSnapshot(object):
1883
1909
  max_partitions=None,
1884
1910
  query_options=None,
1885
1911
  data_boost_enabled=False,
1912
+ lazy_decode=False,
1886
1913
  ):
1887
1914
  """Start a partitioned query operation to get list of partitions and
1888
1915
  then executes each partition on a separate thread
@@ -1943,7 +1970,7 @@ class BatchSnapshot(object):
1943
1970
  data_boost_enabled,
1944
1971
  )
1945
1972
  )
1946
- return MergedResultSet(self, partitions, 0)
1973
+ return MergedResultSet(self, partitions, 0, lazy_decode=lazy_decode)
1947
1974
 
1948
1975
  def process(self, batch):
1949
1976
  """Process a single, partitioned query or read.
@@ -13,4 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- __version__ = "3.58.0" # {x-release-please-version}
16
+ __version__ = "3.59.0" # {x-release-please-version}
@@ -33,10 +33,13 @@ class PartitionExecutor:
33
33
  rows in the queue
34
34
  """
35
35
 
36
- def __init__(self, batch_snapshot, partition_id, merged_result_set):
36
+ def __init__(
37
+ self, batch_snapshot, partition_id, merged_result_set, lazy_decode=False
38
+ ):
37
39
  self._batch_snapshot: BatchSnapshot = batch_snapshot
38
40
  self._partition_id = partition_id
39
41
  self._merged_result_set: MergedResultSet = merged_result_set
42
+ self._lazy_decode = lazy_decode
40
43
  self._queue: Queue[PartitionExecutorResult] = merged_result_set._queue
41
44
 
42
45
  def run(self):
@@ -52,7 +55,9 @@ class PartitionExecutor:
52
55
  def __run(self):
53
56
  results = None
54
57
  try:
55
- results = self._batch_snapshot.process_query_batch(self._partition_id)
58
+ results = self._batch_snapshot.process_query_batch(
59
+ self._partition_id, lazy_decode=self._lazy_decode
60
+ )
56
61
  for row in results:
57
62
  if self._merged_result_set._metadata is None:
58
63
  self._set_metadata(results)
@@ -75,6 +80,7 @@ class PartitionExecutor:
75
80
  try:
76
81
  if not is_exception:
77
82
  self._merged_result_set._metadata = results.metadata
83
+ self._merged_result_set._result_set = results
78
84
  finally:
79
85
  self._merged_result_set.metadata_lock.release()
80
86
  self._merged_result_set.metadata_event.set()
@@ -94,7 +100,10 @@ class MergedResultSet:
94
100
  records in the MergedResultSet is not guaranteed.
95
101
  """
96
102
 
97
- def __init__(self, batch_snapshot, partition_ids, max_parallelism):
103
+ def __init__(
104
+ self, batch_snapshot, partition_ids, max_parallelism, lazy_decode=False
105
+ ):
106
+ self._result_set = None
98
107
  self._exception = None
99
108
  self._metadata = None
100
109
  self.metadata_event = Event()
@@ -110,7 +119,7 @@ class MergedResultSet:
110
119
  partition_executors = []
111
120
  for partition_id in partition_ids:
112
121
  partition_executors.append(
113
- PartitionExecutor(batch_snapshot, partition_id, self)
122
+ PartitionExecutor(batch_snapshot, partition_id, self, lazy_decode)
114
123
  )
115
124
  executor = ThreadPoolExecutor(max_workers=parallelism)
116
125
  for partition_executor in partition_executors:
@@ -144,3 +153,27 @@ class MergedResultSet:
144
153
  def stats(self):
145
154
  # TODO: Implement
146
155
  return None
156
+
157
+ def decode_row(self, row: []) -> []:
158
+ """Decodes a row from protobuf values to Python objects. This function
159
+ should only be called for result sets that use ``lazy_decoding=True``.
160
+ The array that is returned by this function is the same as the array
161
+ that would have been returned by the rows iterator if ``lazy_decoding=False``.
162
+
163
+ :returns: an array containing the decoded values of all the columns in the given row
164
+ """
165
+ if self._result_set is None:
166
+ raise ValueError("iterator not started")
167
+ return self._result_set.decode_row(row)
168
+
169
+ def decode_column(self, row: [], column_index: int):
170
+ """Decodes a column from a protobuf value to a Python object. This function
171
+ should only be called for result sets that use ``lazy_decoding=True``.
172
+ The object that is returned by this function is the same as the object
173
+ that would have been returned by the rows iterator if ``lazy_decoding=False``.
174
+
175
+ :returns: the decoded column value
176
+ """
177
+ if self._result_set is None:
178
+ raise ValueError("iterator not started")
179
+ return self._result_set.decode_column(row, column_index)
@@ -17,6 +17,7 @@
17
17
 
18
18
  from .metrics_tracer_factory import MetricsTracerFactory
19
19
  import os
20
+ import logging
20
21
  from .constants import (
21
22
  SPANNER_SERVICE_NAME,
22
23
  GOOGLE_CLOUD_REGION_KEY,
@@ -33,9 +34,6 @@ try:
33
34
 
34
35
  import mmh3
35
36
 
36
- # Override Resource detector logging to not warn when GCP resources are not detected
37
- import logging
38
-
39
37
  logging.getLogger("opentelemetry.resourcedetector.gcp_resource_detector").setLevel(
40
38
  logging.ERROR
41
39
  )
@@ -48,6 +46,8 @@ from .metrics_tracer import MetricsTracer
48
46
  from google.cloud.spanner_v1 import __version__
49
47
  from uuid import uuid4
50
48
 
49
+ log = logging.getLogger(__name__)
50
+
51
51
 
52
52
  class SpannerMetricsTracerFactory(MetricsTracerFactory):
53
53
  """A factory for creating SpannerMetricsTracer instances."""
@@ -158,15 +158,23 @@ class SpannerMetricsTracerFactory(MetricsTracerFactory):
158
158
  def _get_location() -> str:
159
159
  """Get the location of the resource.
160
160
 
161
+ In case of any error during detection, this method will log a warning
162
+ and default to the "global" location.
163
+
161
164
  Returns:
162
165
  str: The location of the resource. If OpenTelemetry is not installed, returns a global region.
163
166
  """
164
167
  if not HAS_OPENTELEMETRY_INSTALLED:
165
168
  return GOOGLE_CLOUD_REGION_GLOBAL
166
- detector = gcp_resource_detector.GoogleCloudResourceDetector()
167
- resources = detector.detect()
168
-
169
- if GOOGLE_CLOUD_REGION_KEY not in resources.attributes:
170
- return GOOGLE_CLOUD_REGION_GLOBAL
171
- else:
172
- return resources[GOOGLE_CLOUD_REGION_KEY]
169
+ try:
170
+ detector = gcp_resource_detector.GoogleCloudResourceDetector()
171
+ resources = detector.detect()
172
+
173
+ if GOOGLE_CLOUD_REGION_KEY in resources.attributes:
174
+ return resources.attributes[GOOGLE_CLOUD_REGION_KEY]
175
+ except Exception as e:
176
+ log.warning(
177
+ "Failed to detect GCP resource location for Spanner metrics, defaulting to 'global'. Error: %s",
178
+ e,
179
+ )
180
+ return GOOGLE_CLOUD_REGION_GLOBAL
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-cloud-spanner
3
- Version: 3.58.0
3
+ Version: 3.59.0
4
4
  Summary: Google Cloud Spanner API client library
5
5
  Home-page: https://github.com/googleapis/python-spanner
6
6
  Author: Google LLC
@@ -167,6 +167,7 @@ tests/unit/test_database_session_manager.py
167
167
  tests/unit/test_datatypes.py
168
168
  tests/unit/test_instance.py
169
169
  tests/unit/test_keyset.py
170
+ tests/unit/test_merged_result_set.py
170
171
  tests/unit/test_metrics.py
171
172
  tests/unit/test_metrics_capture.py
172
173
  tests/unit/test_metrics_exporter.py
@@ -255,6 +255,29 @@ class TestClient(unittest.TestCase):
255
255
  expected_scopes, creds, directed_read_options=self.DIRECTED_READ_OPTIONS
256
256
  )
257
257
 
258
+ @mock.patch.dict(os.environ, {"SPANNER_ENABLE_BUILTIN_METRICS": "true"})
259
+ @mock.patch("google.cloud.spanner_v1.client.SpannerMetricsTracerFactory")
260
+ def test_constructor_w_metrics_initialization_error(
261
+ self, mock_spanner_metrics_factory
262
+ ):
263
+ """
264
+ Test that Client constructor handles exceptions during metrics
265
+ initialization and logs a warning.
266
+ """
267
+ from google.cloud.spanner_v1.client import Client
268
+
269
+ mock_spanner_metrics_factory.side_effect = Exception("Metrics init failed")
270
+ creds = build_scoped_credentials()
271
+
272
+ with self.assertLogs("google.cloud.spanner_v1.client", level="WARNING") as log:
273
+ client = Client(project=self.PROJECT, credentials=creds)
274
+ self.assertIsNotNone(client)
275
+ self.assertIn(
276
+ "Failed to initialize Spanner built-in metrics. Error: Metrics init failed",
277
+ log.output[0],
278
+ )
279
+ mock_spanner_metrics_factory.assert_called_once()
280
+
258
281
  def test_constructor_route_to_leader_disbled(self):
259
282
  from google.cloud.spanner_v1 import client as MUT
260
283
 
@@ -3141,6 +3141,7 @@ class TestBatchSnapshot(_BaseTest):
3141
3141
  params=params,
3142
3142
  param_types=param_types,
3143
3143
  partition=token,
3144
+ lazy_decode=False,
3144
3145
  retry=gapic_v1.method.DEFAULT,
3145
3146
  timeout=gapic_v1.method.DEFAULT,
3146
3147
  )
@@ -3170,6 +3171,7 @@ class TestBatchSnapshot(_BaseTest):
3170
3171
  params=params,
3171
3172
  param_types=param_types,
3172
3173
  partition=token,
3174
+ lazy_decode=False,
3173
3175
  retry=retry,
3174
3176
  timeout=2.0,
3175
3177
  )
@@ -3193,11 +3195,23 @@ class TestBatchSnapshot(_BaseTest):
3193
3195
  snapshot.execute_sql.assert_called_once_with(
3194
3196
  sql=sql,
3195
3197
  partition=token,
3198
+ lazy_decode=False,
3196
3199
  retry=gapic_v1.method.DEFAULT,
3197
3200
  timeout=gapic_v1.method.DEFAULT,
3198
3201
  directed_read_options=DIRECTED_READ_OPTIONS,
3199
3202
  )
3200
3203
 
3204
+ def test_context_manager(self):
3205
+ database = self._make_database()
3206
+ batch_txn = self._make_one(database)
3207
+ session = batch_txn._session = self._make_session()
3208
+ session.is_multiplexed = False
3209
+
3210
+ with batch_txn:
3211
+ pass
3212
+
3213
+ session.delete.assert_called_once_with()
3214
+
3201
3215
  def test_close_wo_session(self):
3202
3216
  database = self._make_database()
3203
3217
  batch_txn = self._make_one(database)
@@ -3292,6 +3306,7 @@ class TestBatchSnapshot(_BaseTest):
3292
3306
  params=params,
3293
3307
  param_types=param_types,
3294
3308
  partition=token,
3309
+ lazy_decode=False,
3295
3310
  retry=gapic_v1.method.DEFAULT,
3296
3311
  timeout=gapic_v1.method.DEFAULT,
3297
3312
  )
@@ -0,0 +1,119 @@
1
+ # Copyright 2025 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
+ import unittest
16
+
17
+ import mock
18
+ from google.cloud.spanner_v1.streamed import StreamedResultSet
19
+
20
+
21
+ class TestMergedResultSet(unittest.TestCase):
22
+ def _get_target_class(self):
23
+ from google.cloud.spanner_v1.merged_result_set import MergedResultSet
24
+
25
+ return MergedResultSet
26
+
27
+ def _make_one(self, *args, **kwargs):
28
+ klass = self._get_target_class()
29
+ obj = super(klass, klass).__new__(klass)
30
+ from threading import Event, Lock
31
+
32
+ obj.metadata_event = Event()
33
+ obj.metadata_lock = Lock()
34
+ obj._metadata = None
35
+ obj._result_set = None
36
+ return obj
37
+
38
+ @staticmethod
39
+ def _make_value(value):
40
+ from google.cloud.spanner_v1._helpers import _make_value_pb
41
+
42
+ return _make_value_pb(value)
43
+
44
+ @staticmethod
45
+ def _make_scalar_field(name, type_):
46
+ from google.cloud.spanner_v1 import StructType
47
+ from google.cloud.spanner_v1 import Type
48
+
49
+ return StructType.Field(name=name, type_=Type(code=type_))
50
+
51
+ @staticmethod
52
+ def _make_result_set_metadata(fields=()):
53
+ from google.cloud.spanner_v1 import ResultSetMetadata
54
+ from google.cloud.spanner_v1 import StructType
55
+
56
+ metadata = ResultSetMetadata(row_type=StructType(fields=[]))
57
+ for field in fields:
58
+ metadata.row_type.fields.append(field)
59
+ return metadata
60
+
61
+ def test_stats_property(self):
62
+ merged = self._make_one()
63
+ # The property is currently not implemented, so it should just return None.
64
+ self.assertIsNone(merged.stats)
65
+
66
+ def test_decode_row(self):
67
+ merged = self._make_one()
68
+
69
+ merged._result_set = mock.create_autospec(StreamedResultSet, instance=True)
70
+ merged._result_set.decode_row.return_value = ["Phred", 42]
71
+
72
+ raw_row = [self._make_value("Phred"), self._make_value(42)]
73
+ decoded_row = merged.decode_row(raw_row)
74
+
75
+ self.assertEqual(decoded_row, ["Phred", 42])
76
+ merged._result_set.decode_row.assert_called_once_with(raw_row)
77
+
78
+ def test_decode_row_no_result_set(self):
79
+ merged = self._make_one()
80
+ merged._result_set = None
81
+ with self.assertRaisesRegex(ValueError, "iterator not started"):
82
+ merged.decode_row([])
83
+
84
+ def test_decode_row_type_error(self):
85
+ merged = self._make_one()
86
+ merged._result_set = mock.create_autospec(StreamedResultSet, instance=True)
87
+ merged._result_set.decode_row.side_effect = TypeError
88
+
89
+ with self.assertRaises(TypeError):
90
+ merged.decode_row("not a list")
91
+
92
+ def test_decode_column(self):
93
+ merged = self._make_one()
94
+ merged._result_set = mock.create_autospec(StreamedResultSet, instance=True)
95
+ merged._result_set.decode_column.side_effect = ["Phred", 42]
96
+
97
+ raw_row = [self._make_value("Phred"), self._make_value(42)]
98
+ decoded_name = merged.decode_column(raw_row, 0)
99
+ decoded_age = merged.decode_column(raw_row, 1)
100
+
101
+ self.assertEqual(decoded_name, "Phred")
102
+ self.assertEqual(decoded_age, 42)
103
+ merged._result_set.decode_column.assert_has_calls(
104
+ [mock.call(raw_row, 0), mock.call(raw_row, 1)]
105
+ )
106
+
107
+ def test_decode_column_no_result_set(self):
108
+ merged = self._make_one()
109
+ merged._result_set = None
110
+ with self.assertRaisesRegex(ValueError, "iterator not started"):
111
+ merged.decode_column([], 0)
112
+
113
+ def test_decode_column_type_error(self):
114
+ merged = self._make_one()
115
+ merged._result_set = mock.create_autospec(StreamedResultSet, instance=True)
116
+ merged._result_set.decode_column.side_effect = TypeError
117
+
118
+ with self.assertRaises(TypeError):
119
+ merged.decode_column("not a list", 0)
@@ -13,9 +13,17 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ import pytest
17
+ import unittest
18
+ from unittest import mock
19
+
20
+ from google.cloud.spanner_v1.metrics.constants import GOOGLE_CLOUD_REGION_KEY
16
21
  from google.cloud.spanner_v1.metrics.spanner_metrics_tracer_factory import (
17
22
  SpannerMetricsTracerFactory,
18
23
  )
24
+ from opentelemetry.sdk.resources import Resource
25
+
26
+ pytest.importorskip("opentelemetry")
19
27
 
20
28
 
21
29
  class TestSpannerMetricsTracerFactory:
@@ -48,3 +56,42 @@ class TestSpannerMetricsTracerFactory:
48
56
  location = SpannerMetricsTracerFactory._get_location()
49
57
  assert isinstance(location, str)
50
58
  assert location # Simply asserting for non empty as this can change depending on the instance this test runs in.
59
+
60
+
61
+ class TestSpannerMetricsTracerFactoryGetLocation(unittest.TestCase):
62
+ @mock.patch(
63
+ "opentelemetry.resourcedetector.gcp_resource_detector.GoogleCloudResourceDetector.detect"
64
+ )
65
+ def test_get_location_with_region(self, mock_detect):
66
+ """Test that _get_location returns the region when detected."""
67
+ mock_resource = Resource.create({GOOGLE_CLOUD_REGION_KEY: "us-central1"})
68
+ mock_detect.return_value = mock_resource
69
+
70
+ location = SpannerMetricsTracerFactory._get_location()
71
+ assert location == "us-central1"
72
+
73
+ @mock.patch(
74
+ "opentelemetry.resourcedetector.gcp_resource_detector.GoogleCloudResourceDetector.detect"
75
+ )
76
+ def test_get_location_without_region(self, mock_detect):
77
+ """Test that _get_location returns 'global' when no region is detected."""
78
+ mock_resource = Resource.create({}) # No region attribute
79
+ mock_detect.return_value = mock_resource
80
+
81
+ location = SpannerMetricsTracerFactory._get_location()
82
+ assert location == "global"
83
+
84
+ @mock.patch(
85
+ "opentelemetry.resourcedetector.gcp_resource_detector.GoogleCloudResourceDetector.detect"
86
+ )
87
+ def test_get_location_with_exception(self, mock_detect):
88
+ """Test that _get_location returns 'global' and logs a warning on exception."""
89
+ mock_detect.side_effect = Exception("detector failed")
90
+
91
+ with self.assertLogs(
92
+ "google.cloud.spanner_v1.metrics.spanner_metrics_tracer_factory",
93
+ level="WARNING",
94
+ ) as log:
95
+ location = SpannerMetricsTracerFactory._get_location()
96
+ assert location == "global"
97
+ self.assertIn("Failed to detect GCP resource location", log.output[0])