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.
- google/cloud/spanner.py +47 -0
- google/cloud/spanner_admin_database_v1/__init__.py +146 -0
- google/cloud/spanner_admin_database_v1/gapic_metadata.json +418 -0
- google/cloud/spanner_admin_database_v1/gapic_version.py +16 -0
- google/cloud/spanner_admin_database_v1/py.typed +2 -0
- google/cloud/spanner_admin_database_v1/services/__init__.py +15 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/__init__.py +22 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py +4097 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/client.py +4602 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/pagers.py +989 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/transports/__init__.py +38 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py +820 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py +1303 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py +1688 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py +6512 -0
- google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py +1650 -0
- google/cloud/spanner_admin_database_v1/types/__init__.py +144 -0
- google/cloud/spanner_admin_database_v1/types/backup.py +1106 -0
- google/cloud/spanner_admin_database_v1/types/backup_schedule.py +369 -0
- google/cloud/spanner_admin_database_v1/types/common.py +180 -0
- google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py +1303 -0
- google/cloud/spanner_admin_instance_v1/__init__.py +110 -0
- google/cloud/spanner_admin_instance_v1/gapic_metadata.json +343 -0
- google/cloud/spanner_admin_instance_v1/gapic_version.py +16 -0
- google/cloud/spanner_admin_instance_v1/py.typed +2 -0
- google/cloud/spanner_admin_instance_v1/services/__init__.py +15 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/__init__.py +22 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py +3466 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py +3881 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/pagers.py +856 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/__init__.py +38 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py +545 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py +1347 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py +1539 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py +4834 -0
- google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py +1198 -0
- google/cloud/spanner_admin_instance_v1/types/__init__.py +104 -0
- google/cloud/spanner_admin_instance_v1/types/common.py +99 -0
- google/cloud/spanner_admin_instance_v1/types/spanner_instance_admin.py +2375 -0
- google/cloud/spanner_dbapi/__init__.py +93 -0
- google/cloud/spanner_dbapi/_helpers.py +113 -0
- google/cloud/spanner_dbapi/batch_dml_executor.py +135 -0
- google/cloud/spanner_dbapi/checksum.py +80 -0
- google/cloud/spanner_dbapi/client_side_statement_executor.py +140 -0
- google/cloud/spanner_dbapi/client_side_statement_parser.py +106 -0
- google/cloud/spanner_dbapi/connection.py +818 -0
- google/cloud/spanner_dbapi/cursor.py +609 -0
- google/cloud/spanner_dbapi/exceptions.py +172 -0
- google/cloud/spanner_dbapi/parse_utils.py +392 -0
- google/cloud/spanner_dbapi/parsed_statement.py +63 -0
- google/cloud/spanner_dbapi/parser.py +258 -0
- google/cloud/spanner_dbapi/partition_helper.py +41 -0
- google/cloud/spanner_dbapi/transaction_helper.py +294 -0
- google/cloud/spanner_dbapi/types.py +106 -0
- google/cloud/spanner_dbapi/utils.py +147 -0
- google/cloud/spanner_dbapi/version.py +20 -0
- google/cloud/spanner_v1/__init__.py +154 -0
- google/cloud/spanner_v1/_helpers.py +751 -0
- google/cloud/spanner_v1/_opentelemetry_tracing.py +165 -0
- google/cloud/spanner_v1/backup.py +397 -0
- google/cloud/spanner_v1/batch.py +433 -0
- google/cloud/spanner_v1/client.py +538 -0
- google/cloud/spanner_v1/data_types.py +350 -0
- google/cloud/spanner_v1/database.py +1968 -0
- google/cloud/spanner_v1/database_sessions_manager.py +249 -0
- google/cloud/spanner_v1/gapic_metadata.json +268 -0
- google/cloud/spanner_v1/gapic_version.py +16 -0
- google/cloud/spanner_v1/instance.py +735 -0
- google/cloud/spanner_v1/keyset.py +193 -0
- google/cloud/spanner_v1/merged_result_set.py +146 -0
- google/cloud/spanner_v1/metrics/constants.py +71 -0
- google/cloud/spanner_v1/metrics/metrics_capture.py +75 -0
- google/cloud/spanner_v1/metrics/metrics_exporter.py +384 -0
- google/cloud/spanner_v1/metrics/metrics_interceptor.py +156 -0
- google/cloud/spanner_v1/metrics/metrics_tracer.py +588 -0
- google/cloud/spanner_v1/metrics/metrics_tracer_factory.py +328 -0
- google/cloud/spanner_v1/metrics/spanner_metrics_tracer_factory.py +172 -0
- google/cloud/spanner_v1/param_types.py +110 -0
- google/cloud/spanner_v1/pool.py +813 -0
- google/cloud/spanner_v1/py.typed +2 -0
- google/cloud/spanner_v1/request_id_header.py +64 -0
- google/cloud/spanner_v1/services/__init__.py +15 -0
- google/cloud/spanner_v1/services/spanner/__init__.py +22 -0
- google/cloud/spanner_v1/services/spanner/async_client.py +2205 -0
- google/cloud/spanner_v1/services/spanner/client.py +2624 -0
- google/cloud/spanner_v1/services/spanner/pagers.py +196 -0
- google/cloud/spanner_v1/services/spanner/transports/__init__.py +38 -0
- google/cloud/spanner_v1/services/spanner/transports/base.py +520 -0
- google/cloud/spanner_v1/services/spanner/transports/grpc.py +911 -0
- google/cloud/spanner_v1/services/spanner/transports/grpc_asyncio.py +1144 -0
- google/cloud/spanner_v1/services/spanner/transports/rest.py +3468 -0
- google/cloud/spanner_v1/services/spanner/transports/rest_base.py +981 -0
- google/cloud/spanner_v1/session.py +631 -0
- google/cloud/spanner_v1/session_options.py +133 -0
- google/cloud/spanner_v1/snapshot.py +1057 -0
- google/cloud/spanner_v1/streamed.py +402 -0
- google/cloud/spanner_v1/table.py +181 -0
- google/cloud/spanner_v1/testing/__init__.py +0 -0
- google/cloud/spanner_v1/testing/database_test.py +121 -0
- google/cloud/spanner_v1/testing/interceptors.py +118 -0
- google/cloud/spanner_v1/testing/mock_database_admin.py +38 -0
- google/cloud/spanner_v1/testing/mock_spanner.py +261 -0
- google/cloud/spanner_v1/testing/spanner_database_admin_pb2_grpc.py +1267 -0
- google/cloud/spanner_v1/testing/spanner_pb2_grpc.py +882 -0
- google/cloud/spanner_v1/transaction.py +747 -0
- google/cloud/spanner_v1/types/__init__.py +118 -0
- google/cloud/spanner_v1/types/commit_response.py +94 -0
- google/cloud/spanner_v1/types/keys.py +248 -0
- google/cloud/spanner_v1/types/mutation.py +201 -0
- google/cloud/spanner_v1/types/query_plan.py +220 -0
- google/cloud/spanner_v1/types/result_set.py +379 -0
- google/cloud/spanner_v1/types/spanner.py +1815 -0
- google/cloud/spanner_v1/types/transaction.py +818 -0
- google/cloud/spanner_v1/types/type.py +288 -0
- google_cloud_spanner-3.55.0.dist-info/LICENSE +202 -0
- google_cloud_spanner-3.55.0.dist-info/METADATA +318 -0
- google_cloud_spanner-3.55.0.dist-info/RECORD +119 -0
- google_cloud_spanner-3.55.0.dist-info/WHEEL +5 -0
- google_cloud_spanner-3.55.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Copyright 2020 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
|
+
"""Connection-based DB API for Cloud Spanner."""
|
|
16
|
+
|
|
17
|
+
from google.cloud.spanner_dbapi.connection import Connection
|
|
18
|
+
from google.cloud.spanner_dbapi.connection import connect
|
|
19
|
+
|
|
20
|
+
from google.cloud.spanner_dbapi.cursor import Cursor
|
|
21
|
+
|
|
22
|
+
from google.cloud.spanner_dbapi.exceptions import DatabaseError
|
|
23
|
+
from google.cloud.spanner_dbapi.exceptions import DataError
|
|
24
|
+
from google.cloud.spanner_dbapi.exceptions import Error
|
|
25
|
+
from google.cloud.spanner_dbapi.exceptions import IntegrityError
|
|
26
|
+
from google.cloud.spanner_dbapi.exceptions import InterfaceError
|
|
27
|
+
from google.cloud.spanner_dbapi.exceptions import InternalError
|
|
28
|
+
from google.cloud.spanner_dbapi.exceptions import NotSupportedError
|
|
29
|
+
from google.cloud.spanner_dbapi.exceptions import OperationalError
|
|
30
|
+
from google.cloud.spanner_dbapi.exceptions import ProgrammingError
|
|
31
|
+
from google.cloud.spanner_dbapi.exceptions import Warning
|
|
32
|
+
|
|
33
|
+
from google.cloud.spanner_dbapi.parse_utils import get_param_types
|
|
34
|
+
|
|
35
|
+
from google.cloud.spanner_dbapi.types import BINARY
|
|
36
|
+
from google.cloud.spanner_dbapi.types import DATETIME
|
|
37
|
+
from google.cloud.spanner_dbapi.types import NUMBER
|
|
38
|
+
from google.cloud.spanner_dbapi.types import ROWID
|
|
39
|
+
from google.cloud.spanner_dbapi.types import STRING
|
|
40
|
+
from google.cloud.spanner_dbapi.types import Binary
|
|
41
|
+
from google.cloud.spanner_dbapi.types import Date
|
|
42
|
+
from google.cloud.spanner_dbapi.types import DateFromTicks
|
|
43
|
+
from google.cloud.spanner_dbapi.types import Time
|
|
44
|
+
from google.cloud.spanner_dbapi.types import TimeFromTicks
|
|
45
|
+
from google.cloud.spanner_dbapi.types import Timestamp
|
|
46
|
+
from google.cloud.spanner_dbapi.types import TimestampStr
|
|
47
|
+
from google.cloud.spanner_dbapi.types import TimestampFromTicks
|
|
48
|
+
|
|
49
|
+
from google.cloud.spanner_dbapi.version import DEFAULT_USER_AGENT
|
|
50
|
+
|
|
51
|
+
apilevel = "2.0" # supports DP-API 2.0 level.
|
|
52
|
+
paramstyle = "format" # ANSI C printf format codes, e.g. ...WHERE name=%s.
|
|
53
|
+
|
|
54
|
+
# Threads may share the module, but not connections. This is a paranoid threadsafety
|
|
55
|
+
# level, but it is necessary for starters to use when debugging failures.
|
|
56
|
+
# Eventually once transactions are working properly, we'll update the
|
|
57
|
+
# threadsafety level.
|
|
58
|
+
threadsafety = 1
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
"Connection",
|
|
63
|
+
"connect",
|
|
64
|
+
"Cursor",
|
|
65
|
+
"DatabaseError",
|
|
66
|
+
"DataError",
|
|
67
|
+
"Error",
|
|
68
|
+
"IntegrityError",
|
|
69
|
+
"InterfaceError",
|
|
70
|
+
"InternalError",
|
|
71
|
+
"NotSupportedError",
|
|
72
|
+
"OperationalError",
|
|
73
|
+
"ProgrammingError",
|
|
74
|
+
"Warning",
|
|
75
|
+
"DEFAULT_USER_AGENT",
|
|
76
|
+
"apilevel",
|
|
77
|
+
"paramstyle",
|
|
78
|
+
"threadsafety",
|
|
79
|
+
"get_param_types",
|
|
80
|
+
"Binary",
|
|
81
|
+
"Date",
|
|
82
|
+
"DateFromTicks",
|
|
83
|
+
"Time",
|
|
84
|
+
"TimeFromTicks",
|
|
85
|
+
"Timestamp",
|
|
86
|
+
"TimestampFromTicks",
|
|
87
|
+
"BINARY",
|
|
88
|
+
"STRING",
|
|
89
|
+
"NUMBER",
|
|
90
|
+
"DATETIME",
|
|
91
|
+
"ROWID",
|
|
92
|
+
"TimestampStr",
|
|
93
|
+
]
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Copyright 2020 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
|
+
from google.cloud.spanner_v1 import param_types
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
SQL_LIST_TABLES = """
|
|
19
|
+
SELECT table_name
|
|
20
|
+
FROM information_schema.tables
|
|
21
|
+
WHERE table_catalog = ''
|
|
22
|
+
AND table_schema = @table_schema
|
|
23
|
+
AND table_type = 'BASE TABLE'
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
SQL_LIST_TABLES_AND_VIEWS = """
|
|
27
|
+
SELECT table_name
|
|
28
|
+
FROM information_schema.tables
|
|
29
|
+
WHERE table_catalog = '' AND table_schema = @table_schema
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
SQL_GET_TABLE_COLUMN_SCHEMA = """
|
|
33
|
+
SELECT COLUMN_NAME, IS_NULLABLE, SPANNER_TYPE
|
|
34
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
35
|
+
WHERE TABLE_SCHEMA = @schema_name AND TABLE_NAME = @table_name
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
# This table maps spanner_types to Spanner's data type sizes as per
|
|
39
|
+
# https://cloud.google.com/spanner/docs/data-types#allowable-types
|
|
40
|
+
# It is used to map `display_size` to a known type for Cursor.description
|
|
41
|
+
# after a row fetch.
|
|
42
|
+
# Since ResultMetadata
|
|
43
|
+
# https://cloud.google.com/spanner/docs/reference/rest/v1/ResultSetMetadata
|
|
44
|
+
# does not send back the actual size, we have to lookup the respective size.
|
|
45
|
+
# Some fields' sizes are dependent upon the dynamic data hence aren't sent back
|
|
46
|
+
# by Cloud Spanner.
|
|
47
|
+
CODE_TO_DISPLAY_SIZE = {
|
|
48
|
+
param_types.BOOL.code: 1,
|
|
49
|
+
param_types.DATE.code: 4,
|
|
50
|
+
param_types.FLOAT64.code: 8,
|
|
51
|
+
param_types.FLOAT32.code: 4,
|
|
52
|
+
param_types.INT64.code: 8,
|
|
53
|
+
param_types.TIMESTAMP.code: 12,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ColumnInfo:
|
|
58
|
+
"""Row column description object."""
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
name,
|
|
63
|
+
type_code,
|
|
64
|
+
display_size=None,
|
|
65
|
+
internal_size=None,
|
|
66
|
+
precision=None,
|
|
67
|
+
scale=None,
|
|
68
|
+
null_ok=False,
|
|
69
|
+
):
|
|
70
|
+
self.name = name
|
|
71
|
+
self.type_code = type_code
|
|
72
|
+
self.display_size = display_size
|
|
73
|
+
self.internal_size = internal_size
|
|
74
|
+
self.precision = precision
|
|
75
|
+
self.scale = scale
|
|
76
|
+
self.null_ok = null_ok
|
|
77
|
+
|
|
78
|
+
self.fields = (
|
|
79
|
+
self.name,
|
|
80
|
+
self.type_code,
|
|
81
|
+
self.display_size,
|
|
82
|
+
self.internal_size,
|
|
83
|
+
self.precision,
|
|
84
|
+
self.scale,
|
|
85
|
+
self.null_ok,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def __repr__(self):
|
|
89
|
+
return self.__str__()
|
|
90
|
+
|
|
91
|
+
def __getitem__(self, index):
|
|
92
|
+
return self.fields[index]
|
|
93
|
+
|
|
94
|
+
def __str__(self):
|
|
95
|
+
str_repr = ", ".join(
|
|
96
|
+
filter(
|
|
97
|
+
lambda part: part is not None,
|
|
98
|
+
[
|
|
99
|
+
"name='%s'" % self.name,
|
|
100
|
+
"type_code=%d" % self.type_code,
|
|
101
|
+
"display_size=%d" % self.display_size
|
|
102
|
+
if self.display_size
|
|
103
|
+
else None,
|
|
104
|
+
"internal_size=%d" % self.internal_size
|
|
105
|
+
if self.internal_size
|
|
106
|
+
else None,
|
|
107
|
+
"precision='%s'" % self.precision if self.precision else None,
|
|
108
|
+
"scale='%s'" % self.scale if self.scale else None,
|
|
109
|
+
"null_ok='%s'" % self.null_ok if self.null_ok else None,
|
|
110
|
+
],
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
return "ColumnInfo(%s)" % str_repr
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Copyright 2023 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
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import TYPE_CHECKING, List
|
|
19
|
+
from google.cloud.spanner_dbapi.parsed_statement import (
|
|
20
|
+
ParsedStatement,
|
|
21
|
+
StatementType,
|
|
22
|
+
Statement,
|
|
23
|
+
)
|
|
24
|
+
from google.rpc.code_pb2 import ABORTED, OK
|
|
25
|
+
from google.api_core.exceptions import Aborted
|
|
26
|
+
|
|
27
|
+
from google.cloud.spanner_dbapi.utils import StreamedManyResultSets
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from google.cloud.spanner_dbapi.cursor import Cursor
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BatchDmlExecutor:
|
|
34
|
+
"""Executor that is used when a DML batch is started. These batches only
|
|
35
|
+
accept DML statements. All DML statements are buffered locally and sent to
|
|
36
|
+
Spanner when runBatch() is called.
|
|
37
|
+
|
|
38
|
+
:type "Cursor": :class:`~google.cloud.spanner_dbapi.cursor.Cursor`
|
|
39
|
+
:param cursor:
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, cursor: "Cursor"):
|
|
43
|
+
self._cursor = cursor
|
|
44
|
+
self._connection = cursor.connection
|
|
45
|
+
self._statements: List[Statement] = []
|
|
46
|
+
|
|
47
|
+
def execute_statement(self, parsed_statement: ParsedStatement):
|
|
48
|
+
"""Executes the statement when dml batch is active by buffering the
|
|
49
|
+
statement in-memory.
|
|
50
|
+
|
|
51
|
+
:type parsed_statement: ParsedStatement
|
|
52
|
+
:param parsed_statement: parsed statement containing sql query and query
|
|
53
|
+
params
|
|
54
|
+
"""
|
|
55
|
+
from google.cloud.spanner_dbapi import ProgrammingError
|
|
56
|
+
|
|
57
|
+
# Note: Let the server handle it if the client-side parser did not
|
|
58
|
+
# recognize the type of statement.
|
|
59
|
+
if (
|
|
60
|
+
parsed_statement.statement_type != StatementType.UPDATE
|
|
61
|
+
and parsed_statement.statement_type != StatementType.INSERT
|
|
62
|
+
and parsed_statement.statement_type != StatementType.UNKNOWN
|
|
63
|
+
):
|
|
64
|
+
raise ProgrammingError("Only DML statements are allowed in batch DML mode.")
|
|
65
|
+
self._statements.append(parsed_statement.statement)
|
|
66
|
+
|
|
67
|
+
def run_batch_dml(self):
|
|
68
|
+
"""Executes all the buffered statements on the active dml batch by
|
|
69
|
+
making a call to Spanner.
|
|
70
|
+
"""
|
|
71
|
+
return run_batch_dml(self._cursor, self._statements)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def run_batch_dml(cursor: "Cursor", statements: List[Statement]):
|
|
75
|
+
"""Executes all the dml statements by making a batch call to Spanner.
|
|
76
|
+
|
|
77
|
+
:type cursor: Cursor
|
|
78
|
+
:param cursor: Database Cursor object
|
|
79
|
+
|
|
80
|
+
:type statements: List[Statement]
|
|
81
|
+
:param statements: list of statements to execute in batch
|
|
82
|
+
"""
|
|
83
|
+
from google.cloud.spanner_dbapi import OperationalError
|
|
84
|
+
|
|
85
|
+
many_result_set = StreamedManyResultSets()
|
|
86
|
+
if not statements:
|
|
87
|
+
return many_result_set
|
|
88
|
+
connection = cursor.connection
|
|
89
|
+
statements_tuple = []
|
|
90
|
+
for statement in statements:
|
|
91
|
+
statements_tuple.append(statement.get_tuple())
|
|
92
|
+
if not connection._client_transaction_started:
|
|
93
|
+
res = connection.database.run_in_transaction(
|
|
94
|
+
_do_batch_update_autocommit, statements_tuple
|
|
95
|
+
)
|
|
96
|
+
many_result_set.add_iter(res)
|
|
97
|
+
cursor._row_count = sum([max(val, 0) for val in res])
|
|
98
|
+
else:
|
|
99
|
+
while True:
|
|
100
|
+
try:
|
|
101
|
+
transaction = connection.transaction_checkout()
|
|
102
|
+
status, res = transaction.batch_update(statements_tuple)
|
|
103
|
+
if status.code == ABORTED:
|
|
104
|
+
connection._transaction = None
|
|
105
|
+
raise Aborted(status.message)
|
|
106
|
+
elif status.code != OK:
|
|
107
|
+
raise OperationalError(status.message)
|
|
108
|
+
|
|
109
|
+
cursor._batch_dml_rows_count = res
|
|
110
|
+
many_result_set.add_iter(res)
|
|
111
|
+
cursor._row_count = sum([max(val, 0) for val in res])
|
|
112
|
+
return many_result_set
|
|
113
|
+
except Aborted:
|
|
114
|
+
# We are raising it so it could be handled in transaction_helper.py and is retried
|
|
115
|
+
if cursor._in_retry_mode:
|
|
116
|
+
raise
|
|
117
|
+
else:
|
|
118
|
+
connection._transaction_helper.retry_transaction()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _do_batch_update_autocommit(transaction, statements):
|
|
122
|
+
from google.cloud.spanner_dbapi import OperationalError
|
|
123
|
+
|
|
124
|
+
status, res = transaction.batch_update(statements, last_statement=True)
|
|
125
|
+
if status.code == ABORTED:
|
|
126
|
+
raise Aborted(status.message)
|
|
127
|
+
elif status.code != OK:
|
|
128
|
+
raise OperationalError(status.message)
|
|
129
|
+
return res
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class BatchMode(Enum):
|
|
133
|
+
DML = 1
|
|
134
|
+
DDL = 2
|
|
135
|
+
NONE = 3
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Copyright 2020 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
|
+
"""API to calculate checksums of SQL statements results."""
|
|
16
|
+
|
|
17
|
+
import hashlib
|
|
18
|
+
import pickle
|
|
19
|
+
|
|
20
|
+
from google.cloud.spanner_dbapi.exceptions import RetryAborted
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ResultsChecksum:
|
|
24
|
+
"""Cumulative checksum.
|
|
25
|
+
|
|
26
|
+
Used to calculate a total checksum of all the results
|
|
27
|
+
returned by operations executed within transaction.
|
|
28
|
+
Includes methods for checksums comparison.
|
|
29
|
+
These checksums are used while retrying an aborted
|
|
30
|
+
transaction to check if the results of a retried transaction
|
|
31
|
+
are equal to the results of the original transaction.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self):
|
|
35
|
+
self.checksum = hashlib.sha256()
|
|
36
|
+
self.count = 0 # counter of consumed results
|
|
37
|
+
|
|
38
|
+
def __len__(self):
|
|
39
|
+
"""Return the number of consumed results.
|
|
40
|
+
|
|
41
|
+
:rtype: :class:`int`
|
|
42
|
+
:returns: The number of results.
|
|
43
|
+
"""
|
|
44
|
+
return self.count
|
|
45
|
+
|
|
46
|
+
def __eq__(self, other):
|
|
47
|
+
"""Check if checksums are equal.
|
|
48
|
+
|
|
49
|
+
:type other: :class:`google.cloud.spanner_dbapi.checksum.ResultsChecksum`
|
|
50
|
+
:param other: Another checksum to compare with this one.
|
|
51
|
+
"""
|
|
52
|
+
return self.checksum.digest() == other.checksum.digest()
|
|
53
|
+
|
|
54
|
+
def consume_result(self, result):
|
|
55
|
+
"""Add the given result into the checksum.
|
|
56
|
+
|
|
57
|
+
:type result: Union[int, list]
|
|
58
|
+
:param result: Streamed row or row count from an UPDATE operation.
|
|
59
|
+
"""
|
|
60
|
+
self.checksum.update(pickle.dumps(result))
|
|
61
|
+
self.count += 1
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _compare_checksums(original, retried):
|
|
65
|
+
from google.cloud.spanner_dbapi.transaction_helper import RETRY_ABORTED_ERROR
|
|
66
|
+
|
|
67
|
+
"""Compare the given checksums.
|
|
68
|
+
|
|
69
|
+
Raise an error if the given checksums are not equal.
|
|
70
|
+
|
|
71
|
+
:type original: :class:`~google.cloud.spanner_dbapi.checksum.ResultsChecksum`
|
|
72
|
+
:param original: results checksum of the original transaction.
|
|
73
|
+
|
|
74
|
+
:type retried: :class:`~google.cloud.spanner_dbapi.checksum.ResultsChecksum`
|
|
75
|
+
:param retried: results checksum of the retried transaction.
|
|
76
|
+
|
|
77
|
+
:raises: :exc:`google.cloud.spanner_dbapi.exceptions.RetryAborted` in case if checksums are not equal.
|
|
78
|
+
"""
|
|
79
|
+
if retried != original:
|
|
80
|
+
raise RetryAborted(RETRY_ABORTED_ERROR)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Copyright 2023 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
|
+
from typing import TYPE_CHECKING, Union
|
|
15
|
+
from google.cloud.spanner_v1 import TransactionOptions
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from google.cloud.spanner_dbapi.cursor import Cursor
|
|
19
|
+
from google.cloud.spanner_dbapi import ProgrammingError
|
|
20
|
+
|
|
21
|
+
from google.cloud.spanner_dbapi.parsed_statement import (
|
|
22
|
+
ParsedStatement,
|
|
23
|
+
ClientSideStatementType,
|
|
24
|
+
)
|
|
25
|
+
from google.cloud.spanner_v1 import (
|
|
26
|
+
Type,
|
|
27
|
+
StructType,
|
|
28
|
+
TypeCode,
|
|
29
|
+
ResultSetMetadata,
|
|
30
|
+
PartialResultSet,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
from google.cloud.spanner_v1._helpers import _make_value_pb
|
|
34
|
+
from google.cloud.spanner_v1.streamed import StreamedResultSet
|
|
35
|
+
|
|
36
|
+
CONNECTION_CLOSED_ERROR = "This connection is closed"
|
|
37
|
+
TRANSACTION_NOT_STARTED_WARNING = (
|
|
38
|
+
"This method is non-operational as a transaction has not been started."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def execute(cursor: "Cursor", parsed_statement: ParsedStatement):
|
|
43
|
+
"""Executes the client side statements by calling the relevant method.
|
|
44
|
+
|
|
45
|
+
It is an internal method that can make backwards-incompatible changes.
|
|
46
|
+
|
|
47
|
+
:type cursor: Cursor
|
|
48
|
+
:param cursor: Cursor object of the dbApi
|
|
49
|
+
|
|
50
|
+
:type parsed_statement: ParsedStatement
|
|
51
|
+
:param parsed_statement: parsed_statement based on the sql query
|
|
52
|
+
"""
|
|
53
|
+
connection = cursor.connection
|
|
54
|
+
column_values = []
|
|
55
|
+
if connection.is_closed:
|
|
56
|
+
raise ProgrammingError(CONNECTION_CLOSED_ERROR)
|
|
57
|
+
statement_type = parsed_statement.client_side_statement_type
|
|
58
|
+
if statement_type == ClientSideStatementType.COMMIT:
|
|
59
|
+
connection.commit()
|
|
60
|
+
return None
|
|
61
|
+
if statement_type == ClientSideStatementType.BEGIN:
|
|
62
|
+
connection.begin(isolation_level=_get_isolation_level(parsed_statement))
|
|
63
|
+
return None
|
|
64
|
+
if statement_type == ClientSideStatementType.ROLLBACK:
|
|
65
|
+
connection.rollback()
|
|
66
|
+
return None
|
|
67
|
+
if statement_type == ClientSideStatementType.SHOW_COMMIT_TIMESTAMP:
|
|
68
|
+
if (
|
|
69
|
+
connection._transaction is not None
|
|
70
|
+
and connection._transaction.committed is not None
|
|
71
|
+
):
|
|
72
|
+
column_values.append(connection._transaction.committed)
|
|
73
|
+
return _get_streamed_result_set(
|
|
74
|
+
ClientSideStatementType.SHOW_COMMIT_TIMESTAMP.name,
|
|
75
|
+
TypeCode.TIMESTAMP,
|
|
76
|
+
column_values,
|
|
77
|
+
)
|
|
78
|
+
if statement_type == ClientSideStatementType.SHOW_READ_TIMESTAMP:
|
|
79
|
+
if (
|
|
80
|
+
connection._snapshot is not None
|
|
81
|
+
and connection._snapshot._transaction_read_timestamp is not None
|
|
82
|
+
):
|
|
83
|
+
column_values.append(connection._snapshot._transaction_read_timestamp)
|
|
84
|
+
return _get_streamed_result_set(
|
|
85
|
+
ClientSideStatementType.SHOW_READ_TIMESTAMP.name,
|
|
86
|
+
TypeCode.TIMESTAMP,
|
|
87
|
+
column_values,
|
|
88
|
+
)
|
|
89
|
+
if statement_type == ClientSideStatementType.START_BATCH_DML:
|
|
90
|
+
connection.start_batch_dml(cursor)
|
|
91
|
+
return None
|
|
92
|
+
if statement_type == ClientSideStatementType.RUN_BATCH:
|
|
93
|
+
return connection.run_batch()
|
|
94
|
+
if statement_type == ClientSideStatementType.ABORT_BATCH:
|
|
95
|
+
return connection.abort_batch()
|
|
96
|
+
if statement_type == ClientSideStatementType.PARTITION_QUERY:
|
|
97
|
+
partition_ids = connection.partition_query(parsed_statement)
|
|
98
|
+
return _get_streamed_result_set(
|
|
99
|
+
"PARTITION",
|
|
100
|
+
TypeCode.STRING,
|
|
101
|
+
partition_ids,
|
|
102
|
+
)
|
|
103
|
+
if statement_type == ClientSideStatementType.RUN_PARTITION:
|
|
104
|
+
return connection.run_partition(
|
|
105
|
+
parsed_statement.client_side_statement_params[0]
|
|
106
|
+
)
|
|
107
|
+
if statement_type == ClientSideStatementType.RUN_PARTITIONED_QUERY:
|
|
108
|
+
return connection.run_partitioned_query(parsed_statement)
|
|
109
|
+
if statement_type == ClientSideStatementType.SET_AUTOCOMMIT_DML_MODE:
|
|
110
|
+
return connection._set_autocommit_dml_mode(parsed_statement)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _get_streamed_result_set(column_name, type_code, column_values):
|
|
114
|
+
struct_type_pb = StructType(
|
|
115
|
+
fields=[StructType.Field(name=column_name, type_=Type(code=type_code))]
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
result_set = PartialResultSet(metadata=ResultSetMetadata(row_type=struct_type_pb))
|
|
119
|
+
if len(column_values) > 0:
|
|
120
|
+
column_values_pb = []
|
|
121
|
+
for column_value in column_values:
|
|
122
|
+
column_values_pb.append(_make_value_pb(column_value))
|
|
123
|
+
result_set.values.extend(column_values_pb)
|
|
124
|
+
return StreamedResultSet(iter([result_set]))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _get_isolation_level(
|
|
128
|
+
statement: ParsedStatement,
|
|
129
|
+
) -> Union[TransactionOptions.IsolationLevel, None]:
|
|
130
|
+
if (
|
|
131
|
+
statement.client_side_statement_params is None
|
|
132
|
+
or len(statement.client_side_statement_params) == 0
|
|
133
|
+
):
|
|
134
|
+
return None
|
|
135
|
+
level = statement.client_side_statement_params[0]
|
|
136
|
+
if not isinstance(level, str) or level == "":
|
|
137
|
+
return None
|
|
138
|
+
# Replace (duplicate) whitespaces in the string with an underscore.
|
|
139
|
+
level = "_".join(level.split()).upper()
|
|
140
|
+
return TransactionOptions.IsolationLevel[level]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Copyright 2023 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 re
|
|
16
|
+
|
|
17
|
+
from google.cloud.spanner_dbapi.parsed_statement import (
|
|
18
|
+
ParsedStatement,
|
|
19
|
+
StatementType,
|
|
20
|
+
ClientSideStatementType,
|
|
21
|
+
Statement,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
RE_BEGIN = re.compile(
|
|
25
|
+
r"^\s*(?:BEGIN|START)(?:\s+TRANSACTION)?(?:\s+ISOLATION\s+LEVEL\s+(REPEATABLE\s+READ|SERIALIZABLE))?\s*$",
|
|
26
|
+
re.IGNORECASE,
|
|
27
|
+
)
|
|
28
|
+
RE_COMMIT = re.compile(r"^\s*(COMMIT)(\s+TRANSACTION)?\s*$", re.IGNORECASE)
|
|
29
|
+
RE_ROLLBACK = re.compile(r"^\s*(ROLLBACK)(\s+TRANSACTION)?\s*$", re.IGNORECASE)
|
|
30
|
+
RE_SHOW_COMMIT_TIMESTAMP = re.compile(
|
|
31
|
+
r"^\s*(SHOW)\s+(VARIABLE)\s+(COMMIT_TIMESTAMP)\s*$", re.IGNORECASE
|
|
32
|
+
)
|
|
33
|
+
RE_SHOW_READ_TIMESTAMP = re.compile(
|
|
34
|
+
r"^\s*(SHOW)\s+(VARIABLE)\s+(READ_TIMESTAMP)\s*$", re.IGNORECASE
|
|
35
|
+
)
|
|
36
|
+
RE_START_BATCH_DML = re.compile(r"^\s*(START)\s+(BATCH)\s+(DML)\s*$", re.IGNORECASE)
|
|
37
|
+
RE_RUN_BATCH = re.compile(r"^\s*(RUN)\s+(BATCH)\s*$", re.IGNORECASE)
|
|
38
|
+
RE_ABORT_BATCH = re.compile(r"^\s*(ABORT)\s+(BATCH)\s*$", re.IGNORECASE)
|
|
39
|
+
RE_PARTITION_QUERY = re.compile(r"^\s*(PARTITION)\s+(.+)", re.IGNORECASE)
|
|
40
|
+
RE_RUN_PARTITION = re.compile(r"^\s*(RUN)\s+(PARTITION)\s+(.+)", re.IGNORECASE)
|
|
41
|
+
RE_RUN_PARTITIONED_QUERY = re.compile(
|
|
42
|
+
r"^\s*(RUN)\s+(PARTITIONED)\s+(QUERY)\s+(.+)", re.IGNORECASE
|
|
43
|
+
)
|
|
44
|
+
RE_SET_AUTOCOMMIT_DML_MODE = re.compile(
|
|
45
|
+
r"^\s*(SET)\s+(AUTOCOMMIT_DML_MODE)\s+(=)\s+(.+)", re.IGNORECASE
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def parse_stmt(query):
|
|
50
|
+
"""Parses the sql query to check if it matches with any of the client side
|
|
51
|
+
statement regex.
|
|
52
|
+
|
|
53
|
+
It is an internal method that can make backwards-incompatible changes.
|
|
54
|
+
|
|
55
|
+
:type query: str
|
|
56
|
+
:param query: sql query
|
|
57
|
+
|
|
58
|
+
:rtype: ParsedStatement
|
|
59
|
+
:returns: ParsedStatement object.
|
|
60
|
+
"""
|
|
61
|
+
client_side_statement_type = None
|
|
62
|
+
client_side_statement_params = []
|
|
63
|
+
if RE_COMMIT.match(query):
|
|
64
|
+
client_side_statement_type = ClientSideStatementType.COMMIT
|
|
65
|
+
elif RE_ROLLBACK.match(query):
|
|
66
|
+
client_side_statement_type = ClientSideStatementType.ROLLBACK
|
|
67
|
+
elif RE_SHOW_COMMIT_TIMESTAMP.match(query):
|
|
68
|
+
client_side_statement_type = ClientSideStatementType.SHOW_COMMIT_TIMESTAMP
|
|
69
|
+
elif RE_SHOW_READ_TIMESTAMP.match(query):
|
|
70
|
+
client_side_statement_type = ClientSideStatementType.SHOW_READ_TIMESTAMP
|
|
71
|
+
elif RE_START_BATCH_DML.match(query):
|
|
72
|
+
client_side_statement_type = ClientSideStatementType.START_BATCH_DML
|
|
73
|
+
elif RE_BEGIN.match(query):
|
|
74
|
+
match = re.search(RE_BEGIN, query)
|
|
75
|
+
isolation_level = match.group(1)
|
|
76
|
+
if isolation_level is not None:
|
|
77
|
+
client_side_statement_params.append(isolation_level)
|
|
78
|
+
client_side_statement_type = ClientSideStatementType.BEGIN
|
|
79
|
+
elif RE_RUN_BATCH.match(query):
|
|
80
|
+
client_side_statement_type = ClientSideStatementType.RUN_BATCH
|
|
81
|
+
elif RE_ABORT_BATCH.match(query):
|
|
82
|
+
client_side_statement_type = ClientSideStatementType.ABORT_BATCH
|
|
83
|
+
elif RE_RUN_PARTITIONED_QUERY.match(query):
|
|
84
|
+
match = re.search(RE_RUN_PARTITIONED_QUERY, query)
|
|
85
|
+
client_side_statement_params.append(match.group(4))
|
|
86
|
+
client_side_statement_type = ClientSideStatementType.RUN_PARTITIONED_QUERY
|
|
87
|
+
elif RE_PARTITION_QUERY.match(query):
|
|
88
|
+
match = re.search(RE_PARTITION_QUERY, query)
|
|
89
|
+
client_side_statement_params.append(match.group(2))
|
|
90
|
+
client_side_statement_type = ClientSideStatementType.PARTITION_QUERY
|
|
91
|
+
elif RE_RUN_PARTITION.match(query):
|
|
92
|
+
match = re.search(RE_RUN_PARTITION, query)
|
|
93
|
+
client_side_statement_params.append(match.group(3))
|
|
94
|
+
client_side_statement_type = ClientSideStatementType.RUN_PARTITION
|
|
95
|
+
elif RE_SET_AUTOCOMMIT_DML_MODE.match(query):
|
|
96
|
+
match = re.search(RE_SET_AUTOCOMMIT_DML_MODE, query)
|
|
97
|
+
client_side_statement_params.append(match.group(4))
|
|
98
|
+
client_side_statement_type = ClientSideStatementType.SET_AUTOCOMMIT_DML_MODE
|
|
99
|
+
if client_side_statement_type is not None:
|
|
100
|
+
return ParsedStatement(
|
|
101
|
+
StatementType.CLIENT_SIDE,
|
|
102
|
+
Statement(query),
|
|
103
|
+
client_side_statement_type,
|
|
104
|
+
client_side_statement_params,
|
|
105
|
+
)
|
|
106
|
+
return None
|