Skip to content

Commit 0b90502

Browse files
committed
dbapi: make Connection.close invoke rollback, guards for already closed
As per PEP-0249 Connection.close, invoke Connection.close() should rollback any uncommitted operations and also make any future uses of the Connection throw dbapi.Error("attempting to use an already closed connection") Currently Connection.rollback() is noop, but with PR #276, we'll have full commit and rollback of transactions. Also while here, make Cursor.close discard anything in the Cursor and unusable thereafter. Fixes #279
1 parent 76d2036 commit 0b90502

2 files changed

Lines changed: 47 additions & 11 deletions

File tree

packages/django-google-spanner/spanner/dbapi/connection.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,30 @@ def __raise_if_already_closed(self):
2222
raise Error('attempting to use an already closed connection')
2323

2424
def close(self):
25-
self.commit()
26-
self.__raise_if_already_closed()
25+
self.rollback()
2726
self.__dbhandle = None
2827
self.__closed = True
2928

3029
def __enter__(self):
3130
return self
3231

3332
def __exit__(self, etype, value, traceback):
34-
return self.close()
33+
self.commit()
34+
self.close()
3535

3636
def commit(self):
37+
self.__raise_if_already_closed()
38+
3739
self.run_prior_DDL_statements()
3840

3941
def rollback(self):
40-
# We don't manage transactions.
41-
pass
42+
self.__raise_if_already_closed()
43+
44+
# TODO: to be added.
4245

4346
def cursor(self):
47+
self.__raise_if_already_closed()
48+
4449
return Cursor(self)
4550

4651
def __handle_update_ddl(self, ddl_statements):
@@ -54,19 +59,29 @@ def __handle_update_ddl(self, ddl_statements):
5459
Returns:
5560
google.api_core.operation.Operation.result()
5661
"""
62+
self.__raise_if_already_closed()
63+
5764
# Synchronously wait on the operation's completion.
5865
return self.__dbhandle.update_ddl(ddl_statements).result()
5966

6067
def read_snapshot(self):
68+
self.__raise_if_already_closed()
69+
6170
return self.__dbhandle.snapshot()
6271

6372
def in_transaction(self, fn, *args, **kwargs):
73+
self.__raise_if_already_closed()
74+
6475
return self.__dbhandle.run_in_transaction(fn, *args, **kwargs)
6576

6677
def append_ddl_statement(self, ddl_statement):
78+
self.__raise_if_already_closed()
79+
6780
self.__ddl_statements.append(ddl_statement)
6881

6982
def run_prior_DDL_statements(self):
83+
self.__raise_if_already_closed()
84+
7085
if not self.__ddl_statements:
7186
return
7287

packages/django-google-spanner/spanner/dbapi/cursor.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
import google.api_core.exceptions as grpc_exceptions
88

9-
from .exceptions import IntegrityError, OperationalError, ProgrammingError
9+
from .exceptions import (
10+
Error, IntegrityError, OperationalError, ProgrammingError,
11+
)
1012
from .parse_utils import (
1113
STMT_DDL, STMT_INSERT, STMT_NON_UPDATING, classify_stmt,
1214
ensure_where_clause, get_param_types, parse_insert,
@@ -23,6 +25,7 @@ def __init__(self, db_handle=None):
2325
self.__row_count = _UNSET_COUNT
2426
self.__db_handle = db_handle
2527
self.__last_op = None
28+
self.__closed = False
2629

2730
# arraysize is a readable and writable property mandated
2831
# by PEP-0249 https://www.python.org/dev/peps/pep-0249/#arraysize
@@ -44,11 +47,16 @@ def description(self):
4447
def rowcount(self):
4548
return self.__row_count
4649

47-
def close(self):
48-
if self.__db_handle is None:
49-
return
50+
def __raise_if_already_closed(self):
51+
"""
52+
Raises an exception if attempting to use an already closed connection.
53+
"""
54+
if self.__closed:
55+
raise Error('attempting to use an already closed connection')
5056

51-
self.__db_handle = None
57+
def close(self):
58+
self.__clear()
59+
self.__closed = True
5260

5361
def execute(self, sql, args=None):
5462
"""
@@ -62,6 +70,8 @@ def execute(self, sql, args=None):
6270
Returns:
6371
None
6472
"""
73+
self.__raise_if_already_closed()
74+
6575
if not self.__db_handle:
6676
raise ProgrammingError('Cursor is not connected to the database')
6777

@@ -186,9 +196,14 @@ def __enter__(self):
186196
return self
187197

188198
def __exit__(self, etype, value, traceback):
189-
self.close()
199+
self.__clear()
200+
201+
def __clear(self):
202+
self.__db_handle = None
190203

191204
def executemany(self, operation, seq_of_params):
205+
self.__raise_if_already_closed()
206+
192207
if not self.__db_handle:
193208
raise ProgrammingError('Cursor is not connected to the database')
194209

@@ -205,12 +220,16 @@ def __iter__(self):
205220
return self.__itr
206221

207222
def fetchone(self):
223+
self.__raise_if_already_closed()
224+
208225
try:
209226
return next(self)
210227
except StopIteration:
211228
return None
212229

213230
def fetchall(self):
231+
self.__raise_if_already_closed()
232+
214233
return list(self.__iter__())
215234

216235
def fetchmany(self, size=None):
@@ -226,6 +245,8 @@ def fetchmany(self, size=None):
226245
Error if the previous call to .execute*() did not produce any result set
227246
or if no call was issued yet.
228247
"""
248+
self.__raise_if_already_closed()
249+
229250
if size is None:
230251
size = self.arraysize
231252

0 commit comments

Comments
 (0)