Uh oh!
There was an error while loading. Please reload this page.
This repository was archived by the owner on Jun 8, 2026. It is now read-only.
- Notifications
You must be signed in to change notification settings - Fork 103
feat: Implementation of client side statements that return#1046
Merged
Uh oh!
There was an error while loading. Please reload this page.
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
3f3b8cb
Implementation of client side statements that return
ankiaga 8b63b9c
Small fix
ankiaga 2361dfb
Incorporated comments
ankiaga 72f6221
Added tests for exception in commit and rollback
ankiaga 27594e5
Fix in tests
ankiaga 9108192
Skipping few tests from running in emulator
ankiaga f688d54
Few fixes
ankiaga 03f42a2
Refactoring
ankiaga e603dc3
Incorporated comments
ankiaga 500b513
Incorporating comments
ankiaga 00df2ef
Merge branch 'main' into clientReturn
ankiaga File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
66 changes: 60 additions & 6 deletions
66 google/cloud/spanner_dbapi/client_side_statement_executor.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 16 additions & 7 deletions
23 google/cloud/spanner_dbapi/client_side_statement_parser.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -23,6 +23,7 @@ | ||
| from google.cloud.spanner_v1 import RequestOptions | ||
| from google.cloud.spanner_v1.session import _get_retry_delay | ||
| from google.cloud.spanner_v1.snapshot import Snapshot | ||
| from deprecated import deprecated | ||
| from google.cloud.spanner_dbapi.checksum import _compare_checksums | ||
| from google.cloud.spanner_dbapi.checksum import ResultsChecksum | ||
| @@ -35,7 +36,7 @@ | ||
| CLIENT_TRANSACTION_NOT_STARTED_WARNING = ( | ||
| "This method is non-operational as transaction has not started" | ||
| "This method is non-operational as a transaction has not been started." | ||
| ) | ||
| MAX_INTERNAL_RETRIES = 50 | ||
| @@ -107,6 +108,9 @@ def __init__(self, instance, database=None, read_only=False): | ||
| self._staleness = None | ||
| self.request_priority = None | ||
| self._transaction_begin_marked = False | ||
| # whether transaction started at Spanner. This means that we had | ||
| # made atleast one call to Spanner. | ||
| self._spanner_transaction_started = False | ||
| @property | ||
| def autocommit(self): | ||
| @@ -140,26 +144,15 @@ def database(self): | ||
| return self._database | ||
| @property | ||
| def _spanner_transaction_started(self): | ||
| """Flag: whether transaction started at Spanner. This means that we had | ||
| made atleast one call to Spanner. Property client_transaction_started | ||
| would always be true if this is true as transaction has to start first | ||
| at clientside than at Spanner | ||
| Returns: | ||
| bool: True if Spanner transaction started, False otherwise. | ||
| """ | ||
| @deprecated( | ||
| reason="This method is deprecated. Use _spanner_transaction_started field" | ||
| ) | ||
| def inside_transaction(self): | ||
| return ( | ||
| self._transaction | ||
| and not self._transaction.committed | ||
| and not self._transaction.rolled_back | ||
| ) or (self._snapshot is not None) | ||
olavloite marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| @property | ||
| def inside_transaction(self): | ||
| """Deprecated property which won't be supported in future versions. | ||
| Please use spanner_transaction_started property instead.""" | ||
| return self._spanner_transaction_started | ||
| ) | ||
| @property | ||
| def _client_transaction_started(self): | ||
| @@ -277,7 +270,8 @@ def _release_session(self): | ||
| """ | ||
| if self.database is None: | ||
| raise ValueError("Database needs to be passed for this operation") | ||
| self.database._pool.put(self._session) | ||
| if self._session is not None: | ||
| self.database._pool.put(self._session) | ||
| self._session = None | ||
| def retry_transaction(self): | ||
| @@ -293,7 +287,7 @@ def retry_transaction(self): | ||
| """ | ||
| attempt = 0 | ||
| while True: | ||
| self._transaction = None | ||
| self._spanner_transaction_started = False | ||
| attempt += 1 | ||
| if attempt > MAX_INTERNAL_RETRIES: | ||
| raise | ||
| @@ -319,7 +313,6 @@ def _rerun_previous_statements(self): | ||
| status, res = transaction.batch_update(statements) | ||
| if status.code == ABORTED: | ||
| self.connection._transaction = None | ||
| raise Aborted(status.details) | ||
| retried_checksum = ResultsChecksum() | ||
| @@ -363,6 +356,8 @@ def transaction_checkout(self): | ||
| if not self.read_only and self._client_transaction_started: | ||
| if not self._spanner_transaction_started: | ||
| self._transaction = self._session_checkout().transaction() | ||
| self._snapshot = None | ||
| self._spanner_transaction_started = True | ||
| self._transaction.begin() | ||
| return self._transaction | ||
| @@ -377,11 +372,13 @@ def snapshot_checkout(self): | ||
| :returns: A Cloud Spanner snapshot object, ready to use. | ||
| """ | ||
| if self.read_only and self._client_transaction_started: | ||
| if not self._snapshot: | ||
| if not self._spanner_transaction_started: | ||
| self._snapshot = Snapshot( | ||
| self._session_checkout(), multi_use=True, **self.staleness | ||
| ) | ||
| self._transaction = None | ||
| self._snapshot.begin() | ||
olavloite marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| self._spanner_transaction_started = True | ||
| return self._snapshot | ||
| @@ -391,7 +388,7 @@ def close(self): | ||
| The connection will be unusable from this point forward. If the | ||
| connection has an active transaction, it will be rolled back. | ||
| """ | ||
| if self._spanner_transaction_started and not self.read_only: | ||
| if self._spanner_transaction_started and not self._read_only: | ||
| self._transaction.rollback() | ||
| if self._own_pool and self.database: | ||
| @@ -405,13 +402,15 @@ def begin(self): | ||
| Marks the transaction as started. | ||
| :raises: :class:`InterfaceError`: if this connection is closed. | ||
| :raises: :class:`OperationalError`: if there is an existing transaction that has begin or is running | ||
| :raises: :class:`OperationalError`: if there is an existing transaction | ||
| that has been started | ||
| """ | ||
| if self._transaction_begin_marked: | ||
| raise OperationalError("A transaction has already started") | ||
| if self._spanner_transaction_started: | ||
| raise OperationalError( | ||
| "Beginning a new transaction is not allowed when a transaction is already running" | ||
| "Beginning a new transaction is not allowed when a transaction " | ||
| "is already running" | ||
| ) | ||
| self._transaction_begin_marked = True | ||
| @@ -430,41 +429,37 @@ def commit(self): | ||
| return | ||
| self.run_prior_DDL_statements() | ||
| if self._spanner_transaction_started: | ||
| try: | ||
| if self.read_only: | ||
| self._snapshot = None | ||
| else: | ||
| self._transaction.commit() | ||
| self._release_session() | ||
| self._statements = [] | ||
| self._transaction_begin_marked = False | ||
| except Aborted: | ||
| self.retry_transaction() | ||
| self.commit() | ||
| try: | ||
| if self._spanner_transaction_started and not self._read_only: | ||
| self._transaction.commit() | ||
| except Aborted: | ||
| self.retry_transaction() | ||
| self.commit() | ||
| finally: | ||
| self._release_session() | ||
| self._statements = [] | ||
| self._transaction_begin_marked = False | ||
| self._spanner_transaction_started = False | ||
| def rollback(self): | ||
| """Rolls back any pending transaction. | ||
| This is a no-op if there is no active client transaction. | ||
| """ | ||
| if not self._client_transaction_started: | ||
| warnings.warn( | ||
| CLIENT_TRANSACTION_NOT_STARTED_WARNING, UserWarning, stacklevel=2 | ||
| ) | ||
| return | ||
| if self._spanner_transaction_started: | ||
| if self.read_only: | ||
| self._snapshot = None | ||
| else: | ||
| try: | ||
| if self._spanner_transaction_started and not self._read_only: | ||
| self._transaction.rollback() | ||
| finally: | ||
| self._release_session() | ||
| self._statements = [] | ||
| self._transaction_begin_marked = False | ||
| self._spanner_transaction_started = False | ||
| @check_not_closed | ||
| def cursor(self): | ||
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.