Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Changeset r15232 refactored transactions so that all transaction stat…

…e is maintained on the connection. This changeset continues that work, moving all transaction control to the connection, too. The transaction control functions in django.db.transaction are left as a generic way to easily apply a transaction control function based on a DB alias. Refs #9964.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15492 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit d1cd53d9cf1c42ee8d1913fe00bb836ee7fa8d6c 1 parent 337a6bc
Russell Keith-Magee authored February 12, 2011
167  django/db/backends/__init__.py
... ...
@@ -1,9 +1,14 @@
1 1
 import decimal
  2
+try:
  3
+    import thread
  4
+except ImportError:
  5
+    import dummy_thread as thread
2 6
 from threading import local
3 7
 
4 8
 from django.conf import settings
5 9
 from django.db import DEFAULT_DB_ALIAS
6 10
 from django.db.backends import util
  11
+from django.db.transaction import TransactionManagementError
7 12
 from django.utils import datetime_safe
8 13
 from django.utils.importlib import import_module
9 14
 
@@ -28,7 +33,7 @@ def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS):
28 33
         # Transaction related attributes
29 34
         self.transaction_state = []
30 35
         self.savepoint_state = 0
31  
-        self.dirty = None
  36
+        self._dirty = None
32 37
 
33 38
     def __eq__(self, other):
34 39
         return self.alias == other.alias
@@ -74,6 +79,166 @@ def _savepoint_commit(self, sid):
74 79
             return
75 80
         self.cursor().execute(self.ops.savepoint_commit_sql(sid))
76 81
 
  82
+    def enter_transaction_management(self, managed=True):
  83
+        """
  84
+        Enters transaction management for a running thread. It must be balanced with
  85
+        the appropriate leave_transaction_management call, since the actual state is
  86
+        managed as a stack.
  87
+
  88
+        The state and dirty flag are carried over from the surrounding block or
  89
+        from the settings, if there is no surrounding block (dirty is always false
  90
+        when no current block is running).
  91
+        """
  92
+        if self.transaction_state:
  93
+            self.transaction_state.append(self.transaction_state[-1])
  94
+        else:
  95
+            self.transaction_state.append(settings.TRANSACTIONS_MANAGED)
  96
+
  97
+        if self._dirty is None:
  98
+            self._dirty = False
  99
+        self._enter_transaction_management(managed)
  100
+
  101
+    def leave_transaction_management(self):
  102
+        """
  103
+        Leaves transaction management for a running thread. A dirty flag is carried
  104
+        over to the surrounding block, as a commit will commit all changes, even
  105
+        those from outside. (Commits are on connection level.)
  106
+        """
  107
+        self._leave_transaction_management(self.is_managed())
  108
+        if self.transaction_state:
  109
+            del self.transaction_state[-1]
  110
+        else:
  111
+            raise TransactionManagementError("This code isn't under transaction "
  112
+                "management")
  113
+        if self._dirty:
  114
+            self.rollback()
  115
+            raise TransactionManagementError("Transaction managed block ended with "
  116
+                "pending COMMIT/ROLLBACK")
  117
+        self._dirty = False
  118
+
  119
+    def is_dirty(self):
  120
+        """
  121
+        Returns True if the current transaction requires a commit for changes to
  122
+        happen.
  123
+        """
  124
+        return self._dirty
  125
+
  126
+    def set_dirty(self):
  127
+        """
  128
+        Sets a dirty flag for the current thread and code streak. This can be used
  129
+        to decide in a managed block of code to decide whether there are open
  130
+        changes waiting for commit.
  131
+        """
  132
+        if self._dirty is not None:
  133
+            self._dirty = True
  134
+        else:
  135
+            raise TransactionManagementError("This code isn't under transaction "
  136
+                "management")
  137
+
  138
+    def set_clean(self):
  139
+        """
  140
+        Resets a dirty flag for the current thread and code streak. This can be used
  141
+        to decide in a managed block of code to decide whether a commit or rollback
  142
+        should happen.
  143
+        """
  144
+        if self._dirty is not None:
  145
+            self._dirty = False
  146
+        else:
  147
+            raise TransactionManagementError("This code isn't under transaction management")
  148
+        self.clean_savepoints()
  149
+
  150
+    def clean_savepoints(self):
  151
+        self.savepoint_state = 0
  152
+
  153
+    def is_managed(self):
  154
+        """
  155
+        Checks whether the transaction manager is in manual or in auto state.
  156
+        """
  157
+        if self.transaction_state:
  158
+            return self.transaction_state[-1]
  159
+        return settings.TRANSACTIONS_MANAGED
  160
+
  161
+    def managed(self, flag=True):
  162
+        """
  163
+        Puts the transaction manager into a manual state: managed transactions have
  164
+        to be committed explicitly by the user. If you switch off transaction
  165
+        management and there is a pending commit/rollback, the data will be
  166
+        commited.
  167
+        """
  168
+        top = self.transaction_state
  169
+        if top:
  170
+            top[-1] = flag
  171
+            if not flag and self.is_dirty():
  172
+                self._commit()
  173
+                self.set_clean()
  174
+        else:
  175
+            raise TransactionManagementError("This code isn't under transaction "
  176
+                "management")
  177
+
  178
+    def commit_unless_managed(self):
  179
+        """
  180
+        Commits changes if the system is not in managed transaction mode.
  181
+        """
  182
+        if not self.is_managed():
  183
+            self._commit()
  184
+            self.clean_savepoints()
  185
+        else:
  186
+            self.set_dirty()
  187
+
  188
+    def rollback_unless_managed(self):
  189
+        """
  190
+        Rolls back changes if the system is not in managed transaction mode.
  191
+        """
  192
+        if not self.is_managed():
  193
+            self._rollback()
  194
+        else:
  195
+            self.set_dirty()
  196
+
  197
+    def commit(self):
  198
+        """
  199
+        Does the commit itself and resets the dirty flag.
  200
+        """
  201
+        self._commit()
  202
+        self.set_clean()
  203
+
  204
+    def rollback(self):
  205
+        """
  206
+        This function does the rollback itself and resets the dirty flag.
  207
+        """
  208
+        self._rollback()
  209
+        self.set_clean()
  210
+
  211
+    def savepoint(self):
  212
+        """
  213
+        Creates a savepoint (if supported and required by the backend) inside the
  214
+        current transaction. Returns an identifier for the savepoint that will be
  215
+        used for the subsequent rollback or commit.
  216
+        """
  217
+        thread_ident = thread.get_ident()
  218
+
  219
+        self.savepoint_state += 1
  220
+
  221
+        tid = str(thread_ident).replace('-', '')
  222
+        sid = "s%s_x%d" % (tid, self.savepoint_state)
  223
+        self._savepoint(sid)
  224
+        return sid
  225
+
  226
+    def savepoint_rollback(self, sid):
  227
+        """
  228
+        Rolls back the most recent savepoint (if one exists). Does nothing if
  229
+        savepoints are not supported.
  230
+        """
  231
+        if self.savepoint_state:
  232
+            self._savepoint_rollback(sid)
  233
+
  234
+    def savepoint_commit(self, sid):
  235
+        """
  236
+        Commits the most recent savepoint (if one exists). Does nothing if
  237
+        savepoints are not supported.
  238
+        """
  239
+        if self.savepoint_state:
  240
+            self._savepoint_commit(sid)
  241
+
77 242
     def close(self):
78 243
         if self.connection is not None:
79 244
             self.connection.close()
95  django/db/transaction.py
@@ -14,10 +14,6 @@
14 14
 import sys
15 15
 
16 16
 try:
17  
-    import thread
18  
-except ImportError:
19  
-    import dummy_thread as thread
20  
-try:
21 17
     from functools import wraps
22 18
 except ImportError:
23 19
     from django.utils.functional import wraps  # Python 2.4 fallback.
@@ -46,15 +42,7 @@ def enter_transaction_management(managed=True, using=None):
46 42
     if using is None:
47 43
         using = DEFAULT_DB_ALIAS
48 44
     connection = connections[using]
49  
-
50  
-    if connection.transaction_state:
51  
-        connection.transaction_state.append(connection.transaction_state[-1])
52  
-    else:
53  
-        connection.transaction_state.append(settings.TRANSACTIONS_MANAGED)
54  
-
55  
-    if connection.dirty is None:
56  
-        connection.dirty = False
57  
-    connection._enter_transaction_management(managed)
  45
+    connection.enter_transaction_management(managed)
58 46
 
59 47
 def leave_transaction_management(using=None):
60 48
     """
@@ -65,18 +53,7 @@ def leave_transaction_management(using=None):
65 53
     if using is None:
66 54
         using = DEFAULT_DB_ALIAS
67 55
     connection = connections[using]
68  
-
69  
-    connection._leave_transaction_management(is_managed(using=using))
70  
-    if connection.transaction_state:
71  
-        del connection.transaction_state[-1]
72  
-    else:
73  
-        raise TransactionManagementError("This code isn't under transaction "
74  
-            "management")
75  
-    if connection.dirty:
76  
-        rollback(using=using)
77  
-        raise TransactionManagementError("Transaction managed block ended with "
78  
-            "pending COMMIT/ROLLBACK")
79  
-    connection.dirty = False
  56
+    connection.leave_transaction_management()
80 57
 
81 58
 def is_dirty(using=None):
82 59
     """
@@ -86,8 +63,7 @@ def is_dirty(using=None):
86 63
     if using is None:
87 64
         using = DEFAULT_DB_ALIAS
88 65
     connection = connections[using]
89  
-
90  
-    return connection.dirty
  66
+    return connection.is_dirty()
91 67
 
92 68
 def set_dirty(using=None):
93 69
     """
@@ -98,12 +74,7 @@ def set_dirty(using=None):
98 74
     if using is None:
99 75
         using = DEFAULT_DB_ALIAS
100 76
     connection = connections[using]
101  
-
102  
-    if connection.dirty is not None:
103  
-        connection.dirty = True
104  
-    else:
105  
-        raise TransactionManagementError("This code isn't under transaction "
106  
-            "management")
  77
+    connection.set_dirty()
107 78
 
108 79
 def set_clean(using=None):
109 80
     """
@@ -114,18 +85,13 @@ def set_clean(using=None):
114 85
     if using is None:
115 86
         using = DEFAULT_DB_ALIAS
116 87
     connection = connections[using]
117  
-
118  
-    if connection.dirty is not None:
119  
-        connection.dirty = False
120  
-    else:
121  
-        raise TransactionManagementError("This code isn't under transaction management")
122  
-    clean_savepoints(using=using)
  88
+    connection.set_clean()
123 89
 
124 90
 def clean_savepoints(using=None):
125 91
     if using is None:
126 92
         using = DEFAULT_DB_ALIAS
127 93
     connection = connections[using]
128  
-    connection.savepoint_state = 0
  94
+    connection.clean_savepoints()
129 95
 
130 96
 def is_managed(using=None):
131 97
     """
@@ -134,9 +100,7 @@ def is_managed(using=None):
134 100
     if using is None:
135 101
         using = DEFAULT_DB_ALIAS
136 102
     connection = connections[using]
137  
-    if connection.transaction_state:
138  
-        return connection.transaction_state[-1]
139  
-    return settings.TRANSACTIONS_MANAGED
  103
+    return connection.is_managed()
140 104
 
141 105
 def managed(flag=True, using=None):
142 106
     """
@@ -148,16 +112,7 @@ def managed(flag=True, using=None):
148 112
     if using is None:
149 113
         using = DEFAULT_DB_ALIAS
150 114
     connection = connections[using]
151  
-
152  
-    top = connection.transaction_state
153  
-    if top:
154  
-        top[-1] = flag
155  
-        if not flag and is_dirty(using=using):
156  
-            connection._commit()
157  
-            set_clean(using=using)
158  
-    else:
159  
-        raise TransactionManagementError("This code isn't under transaction "
160  
-            "management")
  115
+    connection.managed(flag)
161 116
 
162 117
 def commit_unless_managed(using=None):
163 118
     """
@@ -166,11 +121,7 @@ def commit_unless_managed(using=None):
166 121
     if using is None:
167 122
         using = DEFAULT_DB_ALIAS
168 123
     connection = connections[using]
169  
-    if not is_managed(using=using):
170  
-        connection._commit()
171  
-        clean_savepoints(using=using)
172  
-    else:
173  
-        set_dirty(using=using)
  124
+    connection.commit_unless_managed()
174 125
 
175 126
 def rollback_unless_managed(using=None):
176 127
     """
@@ -179,10 +130,7 @@ def rollback_unless_managed(using=None):
179 130
     if using is None:
180 131
         using = DEFAULT_DB_ALIAS
181 132
     connection = connections[using]
182  
-    if not is_managed(using=using):
183  
-        connection._rollback()
184  
-    else:
185  
-        set_dirty(using=using)
  133
+    connection.rollback_unless_managed()
186 134
 
187 135
 def commit(using=None):
188 136
     """
@@ -191,8 +139,7 @@ def commit(using=None):
191 139
     if using is None:
192 140
         using = DEFAULT_DB_ALIAS
193 141
     connection = connections[using]
194  
-    connection._commit()
195  
-    set_clean(using=using)
  142
+    connection.commit()
196 143
 
197 144
 def rollback(using=None):
198 145
     """
@@ -201,8 +148,7 @@ def rollback(using=None):
201 148
     if using is None:
202 149
         using = DEFAULT_DB_ALIAS
203 150
     connection = connections[using]
204  
-    connection._rollback()
205  
-    set_clean(using=using)
  151
+    connection.rollback()
206 152
 
207 153
 def savepoint(using=None):
208 154
     """
@@ -213,14 +159,7 @@ def savepoint(using=None):
213 159
     if using is None:
214 160
         using = DEFAULT_DB_ALIAS
215 161
     connection = connections[using]
216  
-    thread_ident = thread.get_ident()
217  
-
218  
-    connection.savepoint_state += 1
219  
-
220  
-    tid = str(thread_ident).replace('-', '')
221  
-    sid = "s%s_x%d" % (tid, connection.savepoint_state)
222  
-    connection._savepoint(sid)
223  
-    return sid
  162
+    return connection.savepoint()
224 163
 
225 164
 def savepoint_rollback(sid, using=None):
226 165
     """
@@ -230,9 +169,7 @@ def savepoint_rollback(sid, using=None):
230 169
     if using is None:
231 170
         using = DEFAULT_DB_ALIAS
232 171
     connection = connections[using]
233  
-
234  
-    if connection.savepoint_state:
235  
-        connection._savepoint_rollback(sid)
  172
+    connection.savepoint_rollback(sid)
236 173
 
237 174
 def savepoint_commit(sid, using=None):
238 175
     """
@@ -242,9 +179,7 @@ def savepoint_commit(sid, using=None):
242 179
     if using is None:
243 180
         using = DEFAULT_DB_ALIAS
244 181
     connection = connections[using]
245  
-
246  
-    if connection.savepoint_state:
247  
-        connection._savepoint_commit(sid)
  182
+    connection.savepoint_commit(sid)
248 183
 
249 184
 ##############
250 185
 # DECORATORS #

0 notes on commit d1cd53d

Please sign in to comment.
Something went wrong with that request. Please try again.