1414from .cursor import Cursor
1515from .exceptions import InterfaceError
1616
17- AUTOCOMMIT_MODE_WARNING = (
18- "This method is non-operational, as Cloud Spanner"
19- "DB API always works in `autocommit` mode."
20- "See https://github.com/googleapis/python-spanner-django#transaction-management-isnt-supported"
21- )
17+ AUTOCOMMIT_MODE_WARNING = "This method is non-operational in autocommit mode"
2218
2319ColumnDetails = namedtuple ("column_details" , ["null_ok" , "spanner_type" ])
2420
@@ -37,11 +33,98 @@ class Connection:
3733 """
3834
3935 def __init__ (self , instance , database ):
40- self .instance = instance
41- self .database = database
42- self .is_closed = False
36+ self ._instance = instance
37+ self ._database = database
4338
4439 self ._ddl_statements = []
40+ self ._transaction = None
41+ self ._session = None
42+
43+ self .is_closed = False
44+ self ._autocommit = False
45+
46+ @property
47+ def autocommit (self ):
48+ """Autocommit mode flag for this connection.
49+
50+ :rtype: bool
51+ :returns: Autocommit mode flag value.
52+ """
53+ return self ._autocommit
54+
55+ @autocommit .setter
56+ def autocommit (self , value ):
57+ """Change this connection autocommit mode.
58+
59+ :type value: bool
60+ :param value: New autocommit mode state.
61+ """
62+ if value and not self ._autocommit :
63+ self .commit ()
64+
65+ self ._autocommit = value
66+
67+ @property
68+ def database (self ):
69+ """Database to which this connection relates.
70+
71+ :rtype: :class:`~google.cloud.spanner_v1.database.Database`
72+ :returns: The related database object.
73+ """
74+ return self ._database
75+
76+ @property
77+ def instance (self ):
78+ """Instance to which this connection relates.
79+
80+ :rtype: :class:`~google.cloud.spanner_v1.instance.Instance`
81+ :returns: The related instance object.
82+ """
83+ return self ._instance
84+
85+ def _session_checkout (self ):
86+ """Get a Cloud Spanner session from the pool.
87+
88+ If there is already a session associated with
89+ this connection, it'll be used instead.
90+
91+ :rtype: :class:`google.cloud.spanner_v1.session.Session`
92+ :returns: Cloud Spanner session object ready to use.
93+ """
94+ if not self ._session :
95+ self ._session = self .database ._pool .get ()
96+
97+ return self ._session
98+
99+ def _release_session (self ):
100+ """Release the currently used Spanner session.
101+
102+ The session will be returned into the sessions pool.
103+ """
104+ self .database ._pool .put (self ._session )
105+ self ._session = None
106+
107+ def transaction_checkout (self ):
108+ """Get a Cloud Spanner transaction.
109+
110+ Begin a new transaction, if there is no transaction in
111+ this connection yet. Return the begun one otherwise.
112+
113+ The method is non operational in autocommit mode.
114+
115+ :rtype: :class:`google.cloud.spanner_v1.transaction.Transaction`
116+ :returns: A Cloud Spanner transaction object, ready to use.
117+ """
118+ if not self .autocommit :
119+ if (
120+ not self ._transaction
121+ or self ._transaction .committed
122+ or self ._transaction .rolled_back
123+ ):
124+ self ._transaction = self ._session_checkout ().transaction ()
125+ self ._transaction .begin ()
126+
127+ return self ._transaction
45128
46129 def cursor (self ):
47130 self ._raise_if_closed ()
@@ -142,18 +225,33 @@ def get_table_column_schema(self, table_name):
142225 def close (self ):
143226 """Close this connection.
144227
145- The connection will be unusable from this point forward.
228+ The connection will be unusable from this point forward. If the
229+ connection has an active transaction, it will be rolled back.
146230 """
147- self .__dbhandle = None
231+ if (
232+ self ._transaction
233+ and not self ._transaction .committed
234+ and not self ._transaction .rolled_back
235+ ):
236+ self ._transaction .rollback ()
237+
148238 self .is_closed = True
149239
150240 def commit (self ):
151241 """Commit all the pending transactions."""
152- warnings .warn (AUTOCOMMIT_MODE_WARNING , UserWarning , stacklevel = 2 )
242+ if self .autocommit :
243+ warnings .warn (AUTOCOMMIT_MODE_WARNING , UserWarning , stacklevel = 2 )
244+ elif self ._transaction :
245+ self ._transaction .commit ()
246+ self ._release_session ()
153247
154248 def rollback (self ):
155249 """Rollback all the pending transactions."""
156- warnings .warn (AUTOCOMMIT_MODE_WARNING , UserWarning , stacklevel = 2 )
250+ if self .autocommit :
251+ warnings .warn (AUTOCOMMIT_MODE_WARNING , UserWarning , stacklevel = 2 )
252+ elif self ._transaction :
253+ self ._transaction .rollback ()
254+ self ._release_session ()
157255
158256 def __enter__ (self ):
159257 return self
0 commit comments