sqlalchemy-spanner 1.6.2__tar.gz → 1.8.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 (24) hide show
  1. {sqlalchemy-spanner-1.6.2/sqlalchemy_spanner.egg-info → sqlalchemy_spanner-1.8.0}/PKG-INFO +39 -17
  2. sqlalchemy-spanner-1.6.2/PKG-INFO → sqlalchemy_spanner-1.8.0/README.rst +31 -26
  3. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/google/cloud/sqlalchemy_spanner/_opentelemetry_tracing.py +13 -0
  4. sqlalchemy_spanner-1.8.0/google/cloud/sqlalchemy_spanner/dml.py +26 -0
  5. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/google/cloud/sqlalchemy_spanner/requirements.py +1 -1
  6. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/google/cloud/sqlalchemy_spanner/sqlalchemy_spanner.py +235 -26
  7. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/setup.py +25 -21
  8. sqlalchemy-spanner-1.6.2/README.rst → sqlalchemy_spanner-1.8.0/sqlalchemy_spanner.egg-info/PKG-INFO +48 -15
  9. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/sqlalchemy_spanner.egg-info/SOURCES.txt +1 -0
  10. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/test/test_suite_13.py +172 -1
  11. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/test/test_suite_14.py +183 -2
  12. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/test/test_suite_20.py +296 -20
  13. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/LICENSE +0 -0
  14. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/google/__init__.py +0 -0
  15. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/google/cloud/__init__.py +0 -0
  16. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/google/cloud/sqlalchemy_spanner/__init__.py +0 -0
  17. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/google/cloud/sqlalchemy_spanner/provision.py +0 -0
  18. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/setup.cfg +0 -0
  19. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/sqlalchemy_spanner.egg-info/dependency_links.txt +0 -0
  20. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/sqlalchemy_spanner.egg-info/entry_points.txt +0 -0
  21. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/sqlalchemy_spanner.egg-info/namespace_packages.txt +0 -0
  22. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/sqlalchemy_spanner.egg-info/not-zip-safe +0 -0
  23. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/sqlalchemy_spanner.egg-info/requires.txt +0 -0
  24. {sqlalchemy-spanner-1.6.2 → sqlalchemy_spanner-1.8.0}/sqlalchemy_spanner.egg-info/top_level.txt +0 -0
@@ -1,13 +1,19 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sqlalchemy-spanner
3
- Version: 1.6.2
3
+ Version: 1.8.0
4
4
  Summary: SQLAlchemy dialect integrated into Cloud Spanner database
5
5
  Home-page: https://github.com/cloudspannerecosystem/python-spanner-sqlalchemy
6
6
  Author: Google LLC
7
7
  Author-email: cloud-spanner-developers@googlegroups.com
8
8
  Classifier: Intended Audience :: Developers
9
- Provides-Extra: tracing
10
9
  License-File: LICENSE
10
+ Requires-Dist: sqlalchemy>=1.1.13
11
+ Requires-Dist: google-cloud-spanner>=3.12.0
12
+ Requires-Dist: alembic
13
+ Provides-Extra: tracing
14
+ Requires-Dist: opentelemetry-api>=1.1.0; extra == "tracing"
15
+ Requires-Dist: opentelemetry-sdk>=1.1.0; extra == "tracing"
16
+ Requires-Dist: opentelemetry-instrumentation>=0.20b0; extra == "tracing"
11
17
 
12
18
  Spanner dialect for SQLAlchemy
13
19
  ==============================
@@ -69,6 +75,13 @@ Next install the package from the package ``setup.py`` file:
69
75
 
70
76
  During setup the dialect will be registered with entry points.
71
77
 
78
+ Samples
79
+ -------------
80
+
81
+ The `samples directory <https://github.com/googleapis/python-spanner-sqlalchemy/blob/-/samples/README.md>`__
82
+ contains multiple examples for how to configure and use common Spanner features.
83
+
84
+
72
85
  A Minimal App
73
86
  -------------
74
87
 
@@ -84,7 +97,7 @@ on this step in a dialect prefix part:
84
97
  # for SQLAlchemy 1.3:
85
98
  spanner:///projects/project-id/instances/instance-id/databases/database-id
86
99
 
87
- # for SQLAlchemy 1.4:
100
+ # for SQLAlchemy 1.4 and 2.0:
88
101
  spanner+spanner:///projects/project-id/instances/instance-id/databases/database-id
89
102
 
90
103
  To pass your custom client object directly to be be used, create engine as following:
@@ -241,7 +254,7 @@ Unique constraints
241
254
  ~~~~~~~~~~~~~~~~~~
242
255
 
243
256
  Cloud Spanner doesn't support direct UNIQUE constraints creation. In
244
- order to achieve column values uniqueness UNIQUE indexes should be used.
257
+ order to achieve column values uniqueness, UNIQUE indexes should be used.
245
258
 
246
259
  Instead of direct UNIQUE constraint creation:
247
260
 
@@ -269,10 +282,16 @@ Autocommit mode
269
282
  ~~~~~~~~~~~~~~~
270
283
 
271
284
  Spanner dialect supports both ``SERIALIZABLE`` and ``AUTOCOMMIT``
272
- isolation levels. ``SERIALIZABLE`` is the default one, where
273
- transactions need to be committed manually. ``AUTOCOMMIT`` mode
274
- corresponds to automatically committing of a query right in its
275
- execution time.
285
+ isolation levels. ``SERIALIZABLE`` is the default isolation level.
286
+
287
+ ``AUTOCOMMIT`` mode corresponds to automatically committing each
288
+ insert/update/delete statement right after is has been executed.
289
+ Queries that are executed in ``AUTOCOMMIT`` mode use a single-use
290
+ read-only transaction. These do not take any locks and do not need
291
+ to be committed.
292
+
293
+ Workloads that only read data, should use either ``AUTOCOMMIT`` or
294
+ a read-only transaction.
276
295
 
277
296
  Isolation level change example:
278
297
 
@@ -283,7 +302,7 @@ Isolation level change example:
283
302
  eng = create_engine("spanner:///projects/project-id/instances/instance-id/databases/database-id")
284
303
  autocommit_engine = eng.execution_options(isolation_level="AUTOCOMMIT")
285
304
 
286
- Automatic transactions retry
305
+ Automatic transaction retry
287
306
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
288
307
  In the default ``SERIALIZABLE`` mode transactions may fail with ``Aborted`` exception. This is a transient kind of errors, which mostly happen to prevent data corruption by concurrent modifications. Though the original transaction becomes non operational, a simple retry of the queries solves the issue.
289
308
 
@@ -291,8 +310,8 @@ This, however, may require to manually repeat a long list of operations, execute
291
310
 
292
311
  In ``AUTOCOMMIT`` mode automatic transactions retry mechanism is disabled, as every operation is committed just in time, and there is no way an ``Aborted`` exception can happen.
293
312
 
294
- Autoincremented IDs
295
- ~~~~~~~~~~~~~~~~~~~
313
+ Auto-incremented IDs
314
+ ~~~~~~~~~~~~~~~~~~~~
296
315
 
297
316
  Cloud Spanner doesn't support autoincremented IDs mechanism due to
298
317
  performance reasons (`see for more
@@ -348,8 +367,9 @@ ReadOnly transactions
348
367
  ~~~~~~~~~~~~~~~~~~~~~
349
368
 
350
369
  By default, transactions produced by a Spanner connection are in
351
- ReadWrite mode. However, some applications require an ability to grant
352
- ReadOnly access to users/methods; for these cases Spanner dialect
370
+ ReadWrite mode. However, workloads that only read data perform better
371
+ if they use read-only transactions, as Spanner does not need to take
372
+ locks for the data that is read; for these cases, the Spanner dialect
353
373
  supports the ``read_only`` execution option, which switches a connection
354
374
  into ReadOnly mode:
355
375
 
@@ -358,11 +378,13 @@ into ReadOnly mode:
358
378
  with engine.connect().execution_options(read_only=True) as connection:
359
379
  connection.execute(select(["*"], from_obj=table)).fetchall()
360
380
 
361
- Note that execution options are applied lazily - on the ``execute()``
362
- method call, right before it.
381
+ See the `Read-only transaction sample
382
+ <https://github.com/googleapis/python-spanner-sqlalchemy/blob/-/samples/read_only_transaction_sample.py>`__
383
+ for a concrete example.
363
384
 
364
385
  ReadOnly/ReadWrite mode of a connection can't be changed while a
365
- transaction is in progress - first you must commit or rollback it.
386
+ transaction is in progress - you must commit or rollback the current
387
+ transaction before changing the mode.
366
388
 
367
389
  Stale reads
368
390
  ~~~~~~~~~~~
@@ -519,7 +541,7 @@ run the tests the ``nox`` package commands can be used:
519
541
  Running tests on Spanner emulator
520
542
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
521
543
 
522
- The dialect test suite can be runned on `Spanner
544
+ The dialect test suite can be run on `Spanner
523
545
  emulator <https://cloud.google.com/spanner/docs/emulator>`__. Several
524
546
  tests, relating to ``NULL`` values of data types, are skipped when
525
547
  executed on emulator.
@@ -1,14 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: sqlalchemy-spanner
3
- Version: 1.6.2
4
- Summary: SQLAlchemy dialect integrated into Cloud Spanner database
5
- Home-page: https://github.com/cloudspannerecosystem/python-spanner-sqlalchemy
6
- Author: Google LLC
7
- Author-email: cloud-spanner-developers@googlegroups.com
8
- Classifier: Intended Audience :: Developers
9
- Provides-Extra: tracing
10
- License-File: LICENSE
11
-
12
1
  Spanner dialect for SQLAlchemy
13
2
  ==============================
14
3
 
@@ -69,6 +58,13 @@ Next install the package from the package ``setup.py`` file:
69
58
 
70
59
  During setup the dialect will be registered with entry points.
71
60
 
61
+ Samples
62
+ -------------
63
+
64
+ The `samples directory <https://github.com/googleapis/python-spanner-sqlalchemy/blob/-/samples/README.md>`__
65
+ contains multiple examples for how to configure and use common Spanner features.
66
+
67
+
72
68
  A Minimal App
73
69
  -------------
74
70
 
@@ -84,7 +80,7 @@ on this step in a dialect prefix part:
84
80
  # for SQLAlchemy 1.3:
85
81
  spanner:///projects/project-id/instances/instance-id/databases/database-id
86
82
 
87
- # for SQLAlchemy 1.4:
83
+ # for SQLAlchemy 1.4 and 2.0:
88
84
  spanner+spanner:///projects/project-id/instances/instance-id/databases/database-id
89
85
 
90
86
  To pass your custom client object directly to be be used, create engine as following:
@@ -241,7 +237,7 @@ Unique constraints
241
237
  ~~~~~~~~~~~~~~~~~~
242
238
 
243
239
  Cloud Spanner doesn't support direct UNIQUE constraints creation. In
244
- order to achieve column values uniqueness UNIQUE indexes should be used.
240
+ order to achieve column values uniqueness, UNIQUE indexes should be used.
245
241
 
246
242
  Instead of direct UNIQUE constraint creation:
247
243
 
@@ -269,10 +265,16 @@ Autocommit mode
269
265
  ~~~~~~~~~~~~~~~
270
266
 
271
267
  Spanner dialect supports both ``SERIALIZABLE`` and ``AUTOCOMMIT``
272
- isolation levels. ``SERIALIZABLE`` is the default one, where
273
- transactions need to be committed manually. ``AUTOCOMMIT`` mode
274
- corresponds to automatically committing of a query right in its
275
- execution time.
268
+ isolation levels. ``SERIALIZABLE`` is the default isolation level.
269
+
270
+ ``AUTOCOMMIT`` mode corresponds to automatically committing each
271
+ insert/update/delete statement right after is has been executed.
272
+ Queries that are executed in ``AUTOCOMMIT`` mode use a single-use
273
+ read-only transaction. These do not take any locks and do not need
274
+ to be committed.
275
+
276
+ Workloads that only read data, should use either ``AUTOCOMMIT`` or
277
+ a read-only transaction.
276
278
 
277
279
  Isolation level change example:
278
280
 
@@ -283,7 +285,7 @@ Isolation level change example:
283
285
  eng = create_engine("spanner:///projects/project-id/instances/instance-id/databases/database-id")
284
286
  autocommit_engine = eng.execution_options(isolation_level="AUTOCOMMIT")
285
287
 
286
- Automatic transactions retry
288
+ Automatic transaction retry
287
289
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
288
290
  In the default ``SERIALIZABLE`` mode transactions may fail with ``Aborted`` exception. This is a transient kind of errors, which mostly happen to prevent data corruption by concurrent modifications. Though the original transaction becomes non operational, a simple retry of the queries solves the issue.
289
291
 
@@ -291,8 +293,8 @@ This, however, may require to manually repeat a long list of operations, execute
291
293
 
292
294
  In ``AUTOCOMMIT`` mode automatic transactions retry mechanism is disabled, as every operation is committed just in time, and there is no way an ``Aborted`` exception can happen.
293
295
 
294
- Autoincremented IDs
295
- ~~~~~~~~~~~~~~~~~~~
296
+ Auto-incremented IDs
297
+ ~~~~~~~~~~~~~~~~~~~~
296
298
 
297
299
  Cloud Spanner doesn't support autoincremented IDs mechanism due to
298
300
  performance reasons (`see for more
@@ -348,8 +350,9 @@ ReadOnly transactions
348
350
  ~~~~~~~~~~~~~~~~~~~~~
349
351
 
350
352
  By default, transactions produced by a Spanner connection are in
351
- ReadWrite mode. However, some applications require an ability to grant
352
- ReadOnly access to users/methods; for these cases Spanner dialect
353
+ ReadWrite mode. However, workloads that only read data perform better
354
+ if they use read-only transactions, as Spanner does not need to take
355
+ locks for the data that is read; for these cases, the Spanner dialect
353
356
  supports the ``read_only`` execution option, which switches a connection
354
357
  into ReadOnly mode:
355
358
 
@@ -358,11 +361,13 @@ into ReadOnly mode:
358
361
  with engine.connect().execution_options(read_only=True) as connection:
359
362
  connection.execute(select(["*"], from_obj=table)).fetchall()
360
363
 
361
- Note that execution options are applied lazily - on the ``execute()``
362
- method call, right before it.
364
+ See the `Read-only transaction sample
365
+ <https://github.com/googleapis/python-spanner-sqlalchemy/blob/-/samples/read_only_transaction_sample.py>`__
366
+ for a concrete example.
363
367
 
364
368
  ReadOnly/ReadWrite mode of a connection can't be changed while a
365
- transaction is in progress - first you must commit or rollback it.
369
+ transaction is in progress - you must commit or rollback the current
370
+ transaction before changing the mode.
366
371
 
367
372
  Stale reads
368
373
  ~~~~~~~~~~~
@@ -519,7 +524,7 @@ run the tests the ``nox`` package commands can be used:
519
524
  Running tests on Spanner emulator
520
525
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
521
526
 
522
- The dialect test suite can be runned on `Spanner
527
+ The dialect test suite can be run on `Spanner
523
528
  emulator <https://cloud.google.com/spanner/docs/emulator>`__. Several
524
529
  tests, relating to ``NULL`` values of data types, are skipped when
525
530
  executed on emulator.
@@ -14,6 +14,9 @@
14
14
 
15
15
  """Manages OpenTelemetry trace creation and handling"""
16
16
 
17
+ import collections
18
+ import os
19
+
17
20
  from contextlib import contextmanager
18
21
 
19
22
  from google.api_core.exceptions import GoogleAPICallError
@@ -46,6 +49,16 @@ def trace_call(name, extra_attributes=None):
46
49
  }
47
50
 
48
51
  if extra_attributes:
52
+ if os.environ.get("SQLALCHEMY_SPANNER_TRACE_HIDE_QUERY_PARAMETERS"):
53
+ extra_attributes.pop("db.params", None)
54
+
55
+ # Stringify "db.params" sequence values before sending to OpenTelemetry,
56
+ # otherwise OpenTelemetry may log a Warning if types differ.
57
+ if isinstance(extra_attributes, dict):
58
+ for k, v in extra_attributes.items():
59
+ if k == "db.params" and isinstance(v, collections.abc.Sequence):
60
+ extra_attributes[k] = [str(e) for e in v]
61
+
49
62
  attributes.update(extra_attributes)
50
63
 
51
64
  with tracer.start_as_current_span(
@@ -0,0 +1,26 @@
1
+ # Copyright 2024 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 sqlalchemy import Insert, insert
16
+ from sqlalchemy.sql._typing import _DMLTableArgument
17
+
18
+
19
+ def insert_or_update(table: _DMLTableArgument) -> Insert:
20
+ """Construct a Spanner-specific insert-or-update statement."""
21
+ return insert(table).prefix_with("OR UPDATE")
22
+
23
+
24
+ def insert_or_ignore(table: _DMLTableArgument) -> Insert:
25
+ """Construct a Spanner-specific insert-or-ignore statement."""
26
+ return insert(table).prefix_with("OR IGNORE")
@@ -81,7 +81,7 @@ class Requirements(SuiteRequirements): # pragma: no cover
81
81
 
82
82
  @property
83
83
  def sequences(self):
84
- return exclusions.closed()
84
+ return exclusions.open()
85
85
 
86
86
  @property
87
87
  def temporary_tables(self):