Code

Ticket #9964: 9964-against-10087.diff

File 9964-against-10087.diff, 35.3 KB (added by shai, 5 years ago)

Improved patch, tested (tests included)

Line 
1Index: django/db/transaction/fast_select.py
2===================================================================
3--- django/db/transaction/fast_select.py        (revision 0)
4+++ django/db/transaction/fast_select.py        (working copy)
5@@ -10,31 +10,27 @@
6 
7 Managed transactions don't do those commits, but will need some kind of manual
8 or implicit commits or rollbacks.
9+
10+This is a version of the transaction manager which marks transactions
11+as 'clean' or 'dirty', and neglects to close 'clean' transactions to
12+improve performnce.
13+
14+TODO: Explain when this breaks, and when it is less efficient
15 """
16 
17+
18 try:
19     import thread
20 except ImportError:
21     import dummy_thread as thread
22-try:
23-    from functools import wraps
24-except ImportError:
25-    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
26+
27 from django.db import connection
28 from django.conf import settings
29 
30-class TransactionManagementError(Exception):
31-    """
32-    This exception is thrown when something bad happens with transaction
33-    management.
34-    """
35-    pass
36+from django.db.transaction import TransactionManagementError
37+from django.db.transaction import state, savepoint_state
38+from django.db.transaction import is_managed, clean_savepoints, rollback
39 
40-# The states are dictionaries of lists. The key to the dict is the current
41-# thread and the list is handled as a stack of values.
42-state = {}
43-savepoint_state = {}
44-
45 # The dirty flag is set by *_unless_managed functions to denote that the
46 # code under transaction management has changed things to require a
47 # database commit.
48@@ -109,161 +105,3 @@
49         raise TransactionManagementError("This code isn't under transaction management")
50     clean_savepoints()
51 
52-def clean_savepoints():
53-    thread_ident = thread.get_ident()
54-    if thread_ident in savepoint_state:
55-        del savepoint_state[thread_ident]
56-
57-def is_managed():
58-    """
59-    Checks whether the transaction manager is in manual or in auto state.
60-    """
61-    thread_ident = thread.get_ident()
62-    if thread_ident in state:
63-        if state[thread_ident]:
64-            return state[thread_ident][-1]
65-    return settings.TRANSACTIONS_MANAGED
66-
67-def managed(flag=True):
68-    """
69-    Puts the transaction manager into a manual state: managed transactions have
70-    to be committed explicitly by the user. If you switch off transaction
71-    management and there is a pending commit/rollback, the data will be
72-    commited.
73-    """
74-    thread_ident = thread.get_ident()
75-    top = state.get(thread_ident, None)
76-    if top:
77-        top[-1] = flag
78-        if not flag and is_dirty():
79-            connection._commit()
80-            set_clean()
81-    else:
82-        raise TransactionManagementError("This code isn't under transaction management")
83-
84-def commit_unless_managed():
85-    """
86-    Commits changes if the system is not in managed transaction mode.
87-    """
88-    if not is_managed():
89-        connection._commit()
90-        clean_savepoints()
91-    else:
92-        set_dirty()
93-
94-def rollback_unless_managed():
95-    """
96-    Rolls back changes if the system is not in managed transaction mode.
97-    """
98-    if not is_managed():
99-        connection._rollback()
100-    else:
101-        set_dirty()
102-
103-def commit():
104-    """
105-    Does the commit itself and resets the dirty flag.
106-    """
107-    connection._commit()
108-    set_clean()
109-
110-def rollback():
111-    """
112-    This function does the rollback itself and resets the dirty flag.
113-    """
114-    connection._rollback()
115-    set_clean()
116-
117-def savepoint():
118-    """
119-    Creates a savepoint (if supported and required by the backend) inside the
120-    current transaction. Returns an identifier for the savepoint that will be
121-    used for the subsequent rollback or commit.
122-    """
123-    thread_ident = thread.get_ident()
124-    if thread_ident in savepoint_state:
125-        savepoint_state[thread_ident].append(None)
126-    else:
127-        savepoint_state[thread_ident] = [None]
128-    tid = str(thread_ident).replace('-', '')
129-    sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident]))
130-    connection._savepoint(sid)
131-    return sid
132-
133-def savepoint_rollback(sid):
134-    """
135-    Rolls back the most recent savepoint (if one exists). Does nothing if
136-    savepoints are not supported.
137-    """
138-    if thread.get_ident() in savepoint_state:
139-        connection._savepoint_rollback(sid)
140-
141-def savepoint_commit(sid):
142-    """
143-    Commits the most recent savepoint (if one exists). Does nothing if
144-    savepoints are not supported.
145-    """
146-    if thread.get_ident() in savepoint_state:
147-        connection._savepoint_commit(sid)
148-
149-##############
150-# DECORATORS #
151-##############
152-
153-def autocommit(func):
154-    """
155-    Decorator that activates commit on save. This is Django's default behavior;
156-    this decorator is useful if you globally activated transaction management in
157-    your settings file and want the default behavior in some view functions.
158-    """
159-    def _autocommit(*args, **kw):
160-        try:
161-            enter_transaction_management(managed=False)
162-            managed(False)
163-            return func(*args, **kw)
164-        finally:
165-            leave_transaction_management()
166-    return wraps(func)(_autocommit)
167-
168-def commit_on_success(func):
169-    """
170-    This decorator activates commit on response. This way, if the view function
171-    runs successfully, a commit is made; if the viewfunc produces an exception,
172-    a rollback is made. This is one of the most common ways to do transaction
173-    control in web apps.
174-    """
175-    def _commit_on_success(*args, **kw):
176-        try:
177-            enter_transaction_management()
178-            managed(True)
179-            try:
180-                res = func(*args, **kw)
181-            except:
182-                # All exceptions must be handled here (even string ones).
183-                if is_dirty():
184-                    rollback()
185-                raise
186-            else:
187-                if is_dirty():
188-                    commit()
189-            return res
190-        finally:
191-            leave_transaction_management()
192-    return wraps(func)(_commit_on_success)
193-
194-def commit_manually(func):
195-    """
196-    Decorator that activates manual transaction control. It just disables
197-    automatic transaction control and doesn't do any commit/rollback of its
198-    own -- it's up to the user to call the commit and rollback functions
199-    themselves.
200-    """
201-    def _commit_manually(*args, **kw):
202-        try:
203-            enter_transaction_management()
204-            managed(True)
205-            return func(*args, **kw)
206-        finally:
207-            leave_transaction_management()
208-
209-    return wraps(func)(_commit_manually)
210Index: django/db/transaction/safe.py
211===================================================================
212--- django/db/transaction/safe.py       (revision 0)
213+++ django/db/transaction/safe.py       (revision 0)
214@@ -0,0 +1,90 @@
215+"""
216+This module implements a transaction manager that can be used to define
217+transaction handling in a request or view function. It is used by transaction
218+control middleware and decorators.
219+
220+The transaction manager can be in managed or in auto state. Auto state means the
221+system is using a commit-on-save strategy (actually it's more like
222+commit-on-change). As soon as the .save() or .delete() (or related) methods are
223+called, a commit is made.
224+
225+Managed transactions don't do those commits, but will need some kind of manual
226+or implicit commits or rollbacks.
227+
228+This is a version of the transaction manager which treats all transactions
229+as 'dirty', so that they are always closed.
230+"""
231+
232+try:
233+    import thread
234+except ImportError:
235+    import dummy_thread as thread
236+try:
237+    from functools import wraps
238+except ImportError:
239+    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
240+from django.db import connection
241+from django.conf import settings
242+
243+from django.db.transaction import TransactionManagementError
244+from django.db.transaction import state, savepoint_state
245+from django.db.transaction import is_managed, clean_savepoints, rollback
246+
247+
248+def enter_transaction_management(managed=True):
249+    """
250+    Enters transaction management for a running thread. It must be balanced with
251+    the appropriate leave_transaction_management call, since the actual state is
252+    managed as a stack.
253+
254+    The state and dirty flag are carried over from the surrounding block or
255+    from the settings, if there is no surrounding block (dirty is always false
256+    when no current block is running).
257+    """
258+    thread_ident = thread.get_ident()
259+    if thread_ident in state and state[thread_ident]:
260+        state[thread_ident].append(state[thread_ident][-1])
261+    else:
262+        state[thread_ident] = []
263+        state[thread_ident].append(settings.TRANSACTIONS_MANAGED)
264+    connection._enter_transaction_management(managed)
265+
266+def leave_transaction_management():
267+    """
268+    Leaves transaction management for a running thread.
269+    """
270+    connection._leave_transaction_management(is_managed())
271+    thread_ident = thread.get_ident()
272+    if thread_ident in state and state[thread_ident]:
273+        del state[thread_ident][-1]
274+    else:
275+        raise TransactionManagementError("This code isn't under transaction management")
276+    # TODO: This has two drawbacks:
277+    #  - It generates a redundant database hit in most cases
278+    #  - As we no longer tell whether a transaction is dirty,
279+    #    we cannot throw exceptions if a transaction is pending
280+    # To go on the safe side, anything not committed should be rolled back
281+    rollback()
282+
283+def is_dirty():
284+    """
285+    All transactions are always dirty
286+    """
287+    return True
288+
289+def set_dirty():
290+    """
291+    All transactions are always dirty
292+    """
293+    pass
294+
295+def set_clean():
296+    """
297+    All transactions are always dirty
298+    """
299+    # TODO: Really? Retained from the fast_select version
300+    clean_savepoints()
301+
302+
303+
304+
305Index: django/db/transaction/__init__.py
306===================================================================
307--- django/db/transaction/__init__.py   (revision 0)
308+++ django/db/transaction/__init__.py   (revision 0)
309@@ -0,0 +1,274 @@
310+"""
311+This module supports selection, by a setting, of a
312+transaction management "policy". Current policies
313+are "fast-select" (which leaves clean transactions
314+open) and "safe" (which makes sure all transactions
315+are closed, whether they are committed or rolled back.
316+
317+TODO: Document when fast is not safe, and when fast is
318+not even fast (repatable-read w/out MVCC).
319+"""
320+
321+###############
322+# Common base #
323+###############
324+
325+# These are common to the policies
326+try:
327+    import thread
328+except ImportError:
329+    import dummy_thread as thread
330+try:
331+    from functools import wraps
332+except ImportError:
333+    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
334+from django.db import connection
335+from django.conf import settings
336+from django.core.exceptions import ImproperlyConfigured
337+
338+class TransactionManagementError(Exception):
339+    """
340+    This exception is thrown when something bad happens with transaction
341+    management.
342+    """
343+    pass
344+
345+# The states are dictionaries of lists. The key to the dict is the current
346+# thread and the list is handled as a stack of values.
347+state = {}
348+savepoint_state = {}
349+
350+def is_managed():
351+    """
352+    Checks whether the transaction manager is in manual or in auto state.
353+    Note: True means 'managed manually', False means 'auto'
354+    """
355+    thread_ident = thread.get_ident()
356+    if thread_ident in state:
357+        if state[thread_ident]:
358+            return state[thread_ident][-1]
359+    return settings.TRANSACTIONS_MANAGED
360+
361+def managed(flag=True):
362+    """
363+    Puts the transaction manager into a manual state: managed transactions have
364+    to be committed explicitly by the user. If you switch off transaction
365+    management and there is a pending commit/rollback, the data will be
366+    commited.
367+    """
368+    thread_ident = thread.get_ident()
369+    top = state.get(thread_ident, None)
370+    if top:
371+        top[-1] = flag
372+        if not flag and is_dirty():
373+            connection._commit()
374+            set_clean()
375+    else:
376+        raise TransactionManagementError("This code isn't under transaction management")
377+
378+def commit_unless_managed():
379+    """
380+    Commits changes if the system is not in managed transaction mode.
381+    """
382+    if not is_managed():
383+        connection._commit()
384+        clean_savepoints()
385+    else:
386+        set_dirty()
387+
388+def rollback_unless_managed():
389+    """
390+    Rolls back changes if the system is not in managed transaction mode.
391+    """
392+    if not is_managed():
393+        connection._rollback()
394+    else:
395+        set_dirty()
396+
397+def commit():
398+    """
399+    Does the commit itself and resets the dirty flag.
400+    """
401+    connection._commit()
402+    set_clean()
403+
404+def rollback():
405+    """
406+    This function does the rollback itself and resets the dirty flag.
407+    """
408+    connection._rollback()
409+    set_clean()
410+
411+def clean_savepoints():
412+    thread_ident = thread.get_ident()
413+    if thread_ident in savepoint_state:
414+        del savepoint_state[thread_ident]
415+
416+def savepoint():
417+    """
418+    Creates a savepoint (if supported and required by the backend) inside the
419+    current transaction. Returns an identifier for the savepoint that will be
420+    used for the subsequent rollback or commit.
421+    """
422+    thread_ident = thread.get_ident()
423+    if thread_ident in savepoint_state:
424+        savepoint_state[thread_ident].append(None)
425+    else:
426+        savepoint_state[thread_ident] = [None]
427+    tid = str(thread_ident).replace('-', '')
428+    sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident]))
429+    connection._savepoint(sid)
430+    return sid
431+
432+def savepoint_rollback(sid):
433+    """
434+    Rolls back the most recent savepoint (if one exists). Does nothing if
435+    savepoints are not supported.
436+    """
437+    if thread.get_ident() in savepoint_state:
438+        connection._savepoint_rollback(sid)
439+
440+def savepoint_commit(sid):
441+    """
442+    Commits the most recent savepoint (if one exists). Does nothing if
443+    savepoints are not supported.
444+    """
445+    if thread.get_ident() in savepoint_state:
446+        connection._savepoint_commit(sid)
447+
448+#####################
449+# Policy controlled #
450+#####################
451+
452+## DRY-minimizing set of functions for policy control
453+
454+def _get_policy_module():
455+    policy = getattr(settings, 'TRANSACTION_POLICY', 'fast_select')
456+    if policy=='fast_select':
457+        import fast_select
458+        return fast_select
459+    elif policy=='safe':
460+        import safe
461+        return safe
462+    else:
463+        raise ImproperlyConfigured, "Unknown transaction policy '%s'" % str(policy)
464+
465+_to_be_loaded = []
466+
467+def _load_from_policy_module():
468+    module = _get_policy_module()
469+    for name in _to_be_loaded:
470+        globals()[name] = getattr(module, name[1:])
471+
472+
473+def policy_controlled(func):
474+    """
475+    For a function "func", defines a function "_func" which, when
476+    first called, replaces itself with function "func" from the
477+    policy module. Thus, the implementation of func here can
478+    just be written:
479+    def func()
480+        return _func()
481+    policy_controlled(func)
482+    """
483+    place_holder_name = "_" + func.func_name
484+    def place_holder_func(*args, **kwargs):
485+        """
486+        This code is only executed on the first place_holder func to be called
487+        """
488+        #print "policy_controlled, replacing ",place_holder_name
489+        _load_from_policy_module()
490+        real_func = globals()[place_holder_name]
491+        return real_func(*args, **kwargs)
492+    globals()[place_holder_name] = place_holder_func
493+    _to_be_loaded.append(place_holder_name)
494+    return func
495+
496+# The policy controlled functions
497+
498+def enter_transaction_management(managed=True):
499+    return _enter_transaction_management(managed)
500+policy_controlled(enter_transaction_management)
501+
502+def leave_transaction_management():
503+    return _leave_transaction_management()
504+policy_controlled(leave_transaction_management)
505+
506+def is_dirty():
507+    return _is_dirty()
508+policy_controlled(is_dirty)
509+
510+def set_dirty():
511+    return _set_dirty()
512+policy_controlled(set_dirty)
513+
514+def set_clean():
515+    return _set_clean()
516+policy_controlled(set_clean)
517+
518+##############
519+# DECORATORS #
520+##############
521+
522+# Decorators use the above functions, and their own code
523+# doesn't need to change to reflect policy; they are
524+# defined here to make sure they reference the right versions
525+# of the functions they use
526+
527+def autocommit(func):
528+    """
529+    Decorator that activates commit on save. This is Django's default behavior;
530+    this decorator is useful if you globally activated transaction management in
531+    your settings file and want the default behavior in some view functions.
532+    """
533+    def _autocommit(*args, **kw):
534+        try:
535+            enter_transaction_management(managed=False)
536+            managed(False)
537+            return func(*args, **kw)
538+        finally:
539+            leave_transaction_management()
540+    return wraps(func)(_autocommit)
541+
542+def commit_on_success(func):
543+    """
544+    This decorator activates commit on response. This way, if the view function
545+    runs successfully, a commit is made; if the viewfunc produces an exception,
546+    a rollback is made. This is one of the most common ways to do transaction
547+    control in web apps.
548+    """
549+    def _commit_on_success(*args, **kw):
550+        try:
551+            enter_transaction_management()
552+            managed(True)
553+            try:
554+                res = func(*args, **kw)
555+            except:
556+                # All exceptions must be handled here (even string ones).
557+                if is_dirty():
558+                    rollback()
559+                raise
560+            else:
561+                if is_dirty():
562+                    commit()
563+            return res
564+        finally:
565+            leave_transaction_management()
566+    return wraps(func)(_commit_on_success)
567+
568+def commit_manually(func):
569+    """
570+    Decorator that activates manual transaction control. It just disables
571+    automatic transaction control and doesn't do any commit/rollback of its
572+    own -- it's up to the user to call the commit and rollback functions
573+    themselves.
574+    """
575+    def _commit_manually(*args, **kw):
576+        try:
577+            enter_transaction_management()
578+            managed(True)
579+            return func(*args, **kw)
580+        finally:
581+            leave_transaction_management()
582+
583+    return wraps(func)(_commit_manually)
584Index: django/db/transaction.py
585===================================================================
586--- django/db/transaction.py    (revision 10087)
587+++ django/db/transaction.py    (working copy)
588@@ -1,269 +0,0 @@
589-"""
590-This module implements a transaction manager that can be used to define
591-transaction handling in a request or view function. It is used by transaction
592-control middleware and decorators.
593-
594-The transaction manager can be in managed or in auto state. Auto state means the
595-system is using a commit-on-save strategy (actually it's more like
596-commit-on-change). As soon as the .save() or .delete() (or related) methods are
597-called, a commit is made.
598-
599-Managed transactions don't do those commits, but will need some kind of manual
600-or implicit commits or rollbacks.
601-"""
602-
603-try:
604-    import thread
605-except ImportError:
606-    import dummy_thread as thread
607-try:
608-    from functools import wraps
609-except ImportError:
610-    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
611-from django.db import connection
612-from django.conf import settings
613-
614-class TransactionManagementError(Exception):
615-    """
616-    This exception is thrown when something bad happens with transaction
617-    management.
618-    """
619-    pass
620-
621-# The states are dictionaries of lists. The key to the dict is the current
622-# thread and the list is handled as a stack of values.
623-state = {}
624-savepoint_state = {}
625-
626-# The dirty flag is set by *_unless_managed functions to denote that the
627-# code under transaction management has changed things to require a
628-# database commit.
629-dirty = {}
630-
631-def enter_transaction_management(managed=True):
632-    """
633-    Enters transaction management for a running thread. It must be balanced with
634-    the appropriate leave_transaction_management call, since the actual state is
635-    managed as a stack.
636-
637-    The state and dirty flag are carried over from the surrounding block or
638-    from the settings, if there is no surrounding block (dirty is always false
639-    when no current block is running).
640-    """
641-    thread_ident = thread.get_ident()
642-    if thread_ident in state and state[thread_ident]:
643-        state[thread_ident].append(state[thread_ident][-1])
644-    else:
645-        state[thread_ident] = []
646-        state[thread_ident].append(settings.TRANSACTIONS_MANAGED)
647-    if thread_ident not in dirty:
648-        dirty[thread_ident] = False
649-    connection._enter_transaction_management(managed)
650-
651-def leave_transaction_management():
652-    """
653-    Leaves transaction management for a running thread. A dirty flag is carried
654-    over to the surrounding block, as a commit will commit all changes, even
655-    those from outside. (Commits are on connection level.)
656-    """
657-    connection._leave_transaction_management(is_managed())
658-    thread_ident = thread.get_ident()
659-    if thread_ident in state and state[thread_ident]:
660-        del state[thread_ident][-1]
661-    else:
662-        raise TransactionManagementError("This code isn't under transaction management")
663-    if dirty.get(thread_ident, False):
664-        rollback()
665-        raise TransactionManagementError("Transaction managed block ended with pending COMMIT/ROLLBACK")
666-    dirty[thread_ident] = False
667-
668-def is_dirty():
669-    """
670-    Returns True if the current transaction requires a commit for changes to
671-    happen.
672-    """
673-    return dirty.get(thread.get_ident(), False)
674-
675-def set_dirty():
676-    """
677-    Sets a dirty flag for the current thread and code streak. This can be used
678-    to decide in a managed block of code to decide whether there are open
679-    changes waiting for commit.
680-    """
681-    thread_ident = thread.get_ident()
682-    if thread_ident in dirty:
683-        dirty[thread_ident] = True
684-    else:
685-        raise TransactionManagementError("This code isn't under transaction management")
686-
687-def set_clean():
688-    """
689-    Resets a dirty flag for the current thread and code streak. This can be used
690-    to decide in a managed block of code to decide whether a commit or rollback
691-    should happen.
692-    """
693-    thread_ident = thread.get_ident()
694-    if thread_ident in dirty:
695-        dirty[thread_ident] = False
696-    else:
697-        raise TransactionManagementError("This code isn't under transaction management")
698-    clean_savepoints()
699-
700-def clean_savepoints():
701-    thread_ident = thread.get_ident()
702-    if thread_ident in savepoint_state:
703-        del savepoint_state[thread_ident]
704-
705-def is_managed():
706-    """
707-    Checks whether the transaction manager is in manual or in auto state.
708-    """
709-    thread_ident = thread.get_ident()
710-    if thread_ident in state:
711-        if state[thread_ident]:
712-            return state[thread_ident][-1]
713-    return settings.TRANSACTIONS_MANAGED
714-
715-def managed(flag=True):
716-    """
717-    Puts the transaction manager into a manual state: managed transactions have
718-    to be committed explicitly by the user. If you switch off transaction
719-    management and there is a pending commit/rollback, the data will be
720-    commited.
721-    """
722-    thread_ident = thread.get_ident()
723-    top = state.get(thread_ident, None)
724-    if top:
725-        top[-1] = flag
726-        if not flag and is_dirty():
727-            connection._commit()
728-            set_clean()
729-    else:
730-        raise TransactionManagementError("This code isn't under transaction management")
731-
732-def commit_unless_managed():
733-    """
734-    Commits changes if the system is not in managed transaction mode.
735-    """
736-    if not is_managed():
737-        connection._commit()
738-        clean_savepoints()
739-    else:
740-        set_dirty()
741-
742-def rollback_unless_managed():
743-    """
744-    Rolls back changes if the system is not in managed transaction mode.
745-    """
746-    if not is_managed():
747-        connection._rollback()
748-    else:
749-        set_dirty()
750-
751-def commit():
752-    """
753-    Does the commit itself and resets the dirty flag.
754-    """
755-    connection._commit()
756-    set_clean()
757-
758-def rollback():
759-    """
760-    This function does the rollback itself and resets the dirty flag.
761-    """
762-    connection._rollback()
763-    set_clean()
764-
765-def savepoint():
766-    """
767-    Creates a savepoint (if supported and required by the backend) inside the
768-    current transaction. Returns an identifier for the savepoint that will be
769-    used for the subsequent rollback or commit.
770-    """
771-    thread_ident = thread.get_ident()
772-    if thread_ident in savepoint_state:
773-        savepoint_state[thread_ident].append(None)
774-    else:
775-        savepoint_state[thread_ident] = [None]
776-    tid = str(thread_ident).replace('-', '')
777-    sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident]))
778-    connection._savepoint(sid)
779-    return sid
780-
781-def savepoint_rollback(sid):
782-    """
783-    Rolls back the most recent savepoint (if one exists). Does nothing if
784-    savepoints are not supported.
785-    """
786-    if thread.get_ident() in savepoint_state:
787-        connection._savepoint_rollback(sid)
788-
789-def savepoint_commit(sid):
790-    """
791-    Commits the most recent savepoint (if one exists). Does nothing if
792-    savepoints are not supported.
793-    """
794-    if thread.get_ident() in savepoint_state:
795-        connection._savepoint_commit(sid)
796-
797-##############
798-# DECORATORS #
799-##############
800-
801-def autocommit(func):
802-    """
803-    Decorator that activates commit on save. This is Django's default behavior;
804-    this decorator is useful if you globally activated transaction management in
805-    your settings file and want the default behavior in some view functions.
806-    """
807-    def _autocommit(*args, **kw):
808-        try:
809-            enter_transaction_management(managed=False)
810-            managed(False)
811-            return func(*args, **kw)
812-        finally:
813-            leave_transaction_management()
814-    return wraps(func)(_autocommit)
815-
816-def commit_on_success(func):
817-    """
818-    This decorator activates commit on response. This way, if the view function
819-    runs successfully, a commit is made; if the viewfunc produces an exception,
820-    a rollback is made. This is one of the most common ways to do transaction
821-    control in web apps.
822-    """
823-    def _commit_on_success(*args, **kw):
824-        try:
825-            enter_transaction_management()
826-            managed(True)
827-            try:
828-                res = func(*args, **kw)
829-            except:
830-                # All exceptions must be handled here (even string ones).
831-                if is_dirty():
832-                    rollback()
833-                raise
834-            else:
835-                if is_dirty():
836-                    commit()
837-            return res
838-        finally:
839-            leave_transaction_management()
840-    return wraps(func)(_commit_on_success)
841-
842-def commit_manually(func):
843-    """
844-    Decorator that activates manual transaction control. It just disables
845-    automatic transaction control and doesn't do any commit/rollback of its
846-    own -- it's up to the user to call the commit and rollback functions
847-    themselves.
848-    """
849-    def _commit_manually(*args, **kw):
850-        try:
851-            enter_transaction_management()
852-            managed(True)
853-            return func(*args, **kw)
854-        finally:
855-            leave_transaction_management()
856-
857-    return wraps(func)(_commit_manually)
858Index: django/conf/project_template/settings.py
859===================================================================
860--- django/conf/project_template/settings.py    (revision 10087)
861+++ django/conf/project_template/settings.py    (working copy)
862@@ -16,6 +16,9 @@
863 DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
864 DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
865 
866+
867+TRANSACTION_POLICY = 'safe' # Options: 'safe','fast_select'; TODO: better doc.
868+
869 # Local time zone for this installation. Choices can be found here:
870 # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
871 # although not all choices may be available on all operating systems.
872Index: django/conf/global_settings.py
873===================================================================
874--- django/conf/global_settings.py      (revision 10087)
875+++ django/conf/global_settings.py      (working copy)
876@@ -131,6 +131,7 @@
877 DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
878 DATABASE_OPTIONS = {}          # Set to empty dictionary for default.
879 
880+
881 # Host for sending e-mail.
882 EMAIL_HOST = 'localhost'
883 
884@@ -282,8 +283,14 @@
885 
886 # Do you want to manage transactions manually?
887 # Hint: you really don't!
888-TRANSACTIONS_MANAGED = False
889+TRANSACTIONS_MANAGED = False # False means 'automatic'
890 
891+# Django's policy for managed database transactions.
892+# The current options are:
893+#  'safe': always end transaction at the end of request processing.
894+#  'fast_select': leave "clean" transactions open for reuse; pre-1.1 behavior.
895+TRANSACTION_POLICY = 'fast_select'
896+
897 # The User-Agent string to use when checking for URL validity through the
898 # isExistingURL validator.
899 from django import get_version
900Index: tests/modeltests/transactions/models.py
901===================================================================
902--- tests/modeltests/transactions/models.py     (revision 10087)
903+++ tests/modeltests/transactions/models.py     (working copy)
904@@ -86,14 +86,25 @@
905 >>> Reporter.objects.all()
906 [<Reporter: Ben Jones>, <Reporter: Carol Doe>]
907 
908-# If you forget, you'll get bad errors
909+# If you forget, you'll get errors or rollbacks, depending on transaction policy
910 >>> def manually_managed_mistake():
911 ...     r = Reporter(first_name="David", last_name="Davidson")
912 ...     r.save()
913 ...     # oops, I forgot to commit/rollback!
914 >>> manually_managed_mistake = transaction.commit_manually(manually_managed_mistake)
915->>> manually_managed_mistake()
916+>>> manually_managed_mistake()"""
917+
918+# Note: The above string must not include a final newline!
919+
920+if getattr(settings, 'TRANSACTION_POLICY', 'fast_select')!='safe':
921+    __test__['API_TESTS'] += """
922 Traceback (most recent call last):
923     ...
924 TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK
925 """
926+else:
927+    __test__['API_TESTS'] += """
928+>>> Reporter.objects.all()
929+[<Reporter: Ben Jones>, <Reporter: Carol Doe>]
930+
931+"""
932Index: tests/modeltests/transaction_middleware/__init__.py
933===================================================================
934--- tests/modeltests/transaction_middleware/__init__.py (revision 10080)
935+++ tests/modeltests/transaction_middleware/__init__.py (working copy)
936@@ -0,0 +1 @@
937+
938Index: tests/modeltests/transaction_middleware/models.py
939===================================================================
940--- tests/modeltests/transaction_middleware/models.py   (revision 10080)
941+++ tests/modeltests/transaction_middleware/models.py   (working copy)
942@@ -1,10 +1,11 @@
943 """
944-15. Transactions
945+15m. Transaction Middleware
946 
947-Django handles transactions in three different ways. The default is to commit
948-each transaction upon a write, but you can decorate a function to get
949-commit-on-success behavior. Alternatively, you can manage the transaction
950-manually.
951+Django's Transaction Middleware offers a simple consistent way
952+to wrap every request in a transaction. The behavior is supposed
953+to be equivalent to commit_on_success applied to the entire request
954+processing (within the scope of the middleware, of course).
955+These tests are closely related to the transaction tests, of course.
956 """
957 
958 from django.db import models
959@@ -18,6 +19,9 @@
960         return u"%s %s" % (self.first_name, self.last_name)
961 
962 __test__ = {'API_TESTS':"""
963+>>> from django.middleware.transaction import TransactionMiddleware
964+>>> from django.utils.decorators import decorator_from_middleware
965+>>> xact_middle = decorator_from_middleware(TransactionMiddleware)
966 >>> from django.db import connection, transaction
967 """}
968 
969@@ -53,9 +57,10 @@
970 >>> Reporter.objects.all()
971 [<Reporter: Alice Smith>, <Reporter: Ben Jones>]
972 
973-# With the commit_on_success decorator, the transaction is only comitted if the
974+# With the transaction middleware (used here as a decorator),
975+# the transaction is only comitted if the
976 # function doesn't throw an exception
977->>> committed_on_success = transaction.commit_on_success(create_a_reporter_then_fail)
978+>>> committed_on_success = xact_middle(create_a_reporter_then_fail)
979 >>> committed_on_success("Carol", "Doe")
980 Traceback (most recent call last):
981     ...
982@@ -66,34 +71,72 @@
983 [<Reporter: Alice Smith>, <Reporter: Ben Jones>]
984 
985 # If there aren't any exceptions, the data will get saved
986->>> def remove_a_reporter():
987-...     r = Reporter.objects.get(first_name="Alice")
988+>>> def remove_a_reporter(first):
989+...     r = Reporter.objects.get(first_name=first)
990 ...     r.delete()
991 ...
992->>> remove_comitted_on_success = transaction.commit_on_success(remove_a_reporter)
993->>> remove_comitted_on_success()
994+>>> remove_comitted_on_success = xact_middle(remove_a_reporter)
995+>>> remove_comitted_on_success("Alice")
996 >>> Reporter.objects.all()
997 [<Reporter: Ben Jones>]
998+"""
999+if not getattr(settings, 'TRANSACTION_POLICY', None)=='safe':
1000+    __test__['API_TESTS'] += """
1001+# Note there is a trick with custom sql (in fast_select): Executing it doesn't
1002+# set the transaction 'dirty', so it may not be committed
1003+>>> def remove_all_reporters(ignored_request):
1004+...     cursor = connection.cursor()
1005+...     sql = 'delete from ' + Reporter._meta.db_table
1006+...     cursor.execute(sql)
1007+...
1008+>>> remove_all_no_commit = xact_middle(remove_all_reporters)
1009+>>> remove_all_no_commit(None)
1010+>>> Reporter.objects.all()
1011+[]
1012 
1013-# You can manually manage transactions if you really want to, but you
1014-# have to remember to commit/rollback
1015->>> def manually_managed():
1016-...     r = Reporter(first_name="Carol", last_name="Doe")
1017-...     r.save()
1018-...     transaction.commit()
1019->>> manually_managed = transaction.commit_manually(manually_managed)
1020->>> manually_managed()
1021+>>> transaction.rollback()
1022 >>> Reporter.objects.all()
1023-[<Reporter: Ben Jones>, <Reporter: Carol Doe>]
1024+[<Reporter: Ben Jones>]
1025 
1026-# If you forget, you'll get bad errors
1027->>> def manually_managed_mistake():
1028-...     r = Reporter(first_name="David", last_name="Davidson")
1029-...     r.save()
1030-...     # oops, I forgot to commit/rollback!
1031->>> manually_managed_mistake = transaction.commit_manually(manually_managed_mistake)
1032->>> manually_managed_mistake()
1033-Traceback (most recent call last):
1034-    ...
1035-TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK
1036+# To do it properly, you need to set the transaction dirty
1037+# when you execute raw SQL
1038+# set the transaction 'dirty', so it may not be committed
1039+>>> def remove_all_reporters_dirty(ignored_request):
1040+...     cursor = connection.cursor()
1041+...     sql = 'delete from ' + Reporter._meta.db_table
1042+...     cursor.execute(sql)
1043+...     transaction.set_dirty()
1044+...
1045+>>> remove_all_commit = xact_middle(remove_all_reporters_dirty)
1046+>>> remove_all_commit(None)
1047+>>> Reporter.objects.all()
1048+[]
1049+
1050+>>> transaction.rollback()
1051+>>> Reporter.objects.all()
1052+[]
1053 """
1054+else:
1055+    __test__['API_TESTS'] += """
1056+# Under a 'safe' transaction policy, there's no need to set_dirty
1057+>>> def remove_all_reporters(ignored_request):
1058+...     cursor = connection.cursor()
1059+...     sql = 'delete from ' + Reporter._meta.db_table
1060+...     cursor.execute(sql)
1061+...
1062+>>> remove_all_safe = xact_middle(remove_all_reporters)
1063+
1064+# See that we have a reporter:
1065+>>> Reporter.objects.all()
1066+[<Reporter: Ben Jones>]
1067+
1068+>>> remove_all_safe(None)
1069+
1070+# Now we don't, and rollback doesn't get it back
1071+>>> Reporter.objects.all()
1072+[]
1073+>>> transaction.rollback()
1074+>>> Reporter.objects.all()
1075+[]
1076+
1077+"""