Code

Ticket #21134: 21134-failed-attempt.patch

File 21134-failed-attempt.patch, 5.4 KB (added by aaugustin, 7 months ago)
  • django/db/backends/__init__.py

    diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
    index ae9fdbb..add7aa6 100644
    a b class BaseDatabaseWrapper(object): 
    361361            raise TransactionManagementError( 
    362362                "This is forbidden when an 'atomic' block is active.") 
    363363 
     364    def validate_no_broken_transaction(self): 
     365        if self.needs_rollback: 
     366            raise TransactionManagementError( 
     367                "An error occurred in the current transaction. You can't " 
     368                "execute queries until the end of the 'atomic' block.") 
     369 
    364370    def abort(self): 
    365371        """ 
    366372        Roll back any ongoing transaction and clean the transaction state 
  • django/db/backends/utils.py

    diff --git a/django/db/backends/utils.py b/django/db/backends/utils.py
    index 0e2aa45..432f760 100644
    a b class CursorWrapper(object): 
    1919        self.cursor = cursor 
    2020        self.db = db 
    2121 
    22     SET_DIRTY_ATTRS = frozenset(['execute', 'executemany', 'callproc']) 
    23     WRAP_ERROR_ATTRS = frozenset([ 
    24         'callproc', 'close', 'execute', 'executemany', 
    25         'fetchone', 'fetchmany', 'fetchall', 'nextset']) 
     22    WRAP_ERROR_ATTRS = frozenset(['fetchone', 'fetchmany', 'fetchall', 'nextset']) 
    2623 
    2724    def __getattr__(self, attr): 
    28         if attr in CursorWrapper.SET_DIRTY_ATTRS: 
    29             self.db.set_dirty() 
    3025        cursor_attr = getattr(self.cursor, attr) 
    3126        if attr in CursorWrapper.WRAP_ERROR_ATTRS: 
    3227            return self.db.wrap_database_errors(cursor_attr) 
    class CursorWrapper(object): 
    3631    def __iter__(self): 
    3732        return iter(self.cursor) 
    3833 
     34    # The following methods cannot be implemented in __getattr__, because the 
     35    # code must run when the method is invoked, not just when it is accessed. 
    3936 
    40 class CursorDebugWrapper(CursorWrapper): 
     37    def callproc(self, procname, params=None): 
     38        self.db.validate_no_broken_transaction() 
     39        self.db.set_dirty() 
     40        with self.db.wrap_database_errors: 
     41            if params is None: 
     42                return self.cursor.callproc(procname) 
     43            else: 
     44                return self.cursor.callproc(procname, params) 
    4145 
    4246    def execute(self, sql, params=None): 
     47        self.db.validate_no_broken_transaction() 
    4348        self.db.set_dirty() 
     49        with self.db.wrap_database_errors: 
     50            if params is None: 
     51                return self.cursor.execute(sql) 
     52            else: 
     53                return self.cursor.execute(sql, params) 
     54 
     55    def executemany(self, sql, param_list): 
     56        self.db.validate_no_broken_transaction() 
     57        self.db.set_dirty() 
     58        with self.db.wrap_database_errors: 
     59            return self.cursor.executemany(sql, param_list) 
     60 
     61 
     62class CursorDebugWrapper(CursorWrapper): 
     63 
     64    # XXX callproc isn't instrumented at this time. 
     65 
     66    def execute(self, sql, params=None): 
    4467        start = time() 
    4568        try: 
    46             with self.db.wrap_database_errors: 
    47                 if params is None: 
    48                     # params default might be backend specific 
    49                     return self.cursor.execute(sql) 
    50                 return self.cursor.execute(sql, params) 
     69            return super(CursorDebugWrapper, self).execute(sql, params) 
    5170        finally: 
    5271            stop = time() 
    5372            duration = stop - start 
    class CursorDebugWrapper(CursorWrapper): 
    6180            ) 
    6281 
    6382    def executemany(self, sql, param_list): 
    64         self.db.set_dirty() 
    6583        start = time() 
    6684        try: 
    67             with self.db.wrap_database_errors: 
    68                 return self.cursor.executemany(sql, param_list) 
     85            return super(CursorDebugWrapper, self).executemany(sql, param_list) 
    6986        finally: 
    7087            stop = time() 
    7188            duration = stop - start 
  • django/db/transaction.py

    diff --git a/django/db/transaction.py b/django/db/transaction.py
    index 7509ad3..86a357f 100644
    a b import warnings 
    1616 
    1717from functools import wraps 
    1818 
    19 from django.db import connections, DatabaseError, DEFAULT_DB_ALIAS 
     19from django.db import ( 
     20        connections, DEFAULT_DB_ALIAS, 
     21        DatabaseError, ProgrammingError) 
    2022from django.utils.decorators import available_attrs 
    2123 
    2224 
    23 class TransactionManagementError(Exception): 
     25class TransactionManagementError(ProgrammingError): 
    2426    """ 
    25     This exception is thrown when something bad happens with transaction 
    26     management. 
     27    This exception is thrown when transaction management is used improperly. 
    2728    """ 
    2829    pass 
    2930 
  • tests/transactions/tests.py

    diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py
    index 9cf8b4d..9521f60 100644
    a b class AtomicErrorsTests(TransactionTestCase): 
    336336            with self.assertRaises(transaction.TransactionManagementError): 
    337337                transaction.leave_transaction_management() 
    338338 
     339    def test_atomic_prevents_running_more_queries_after_an_error(self): 
     340        cursor = connection.cursor() 
     341        with transaction.atomic(): 
     342            with self.assertRaises(DatabaseError): 
     343                cursor.execute("INSERT INTO transactions_no_such_table (id) VALUES (0)") 
     344            with self.assertRaises(transaction.TransactionManagementError): 
     345                cursor.execute("INSERT INTO transactions_no_such_table (id) VALUES (0)") 
    339346 
    340347class AtomicMiscTests(TransactionTestCase): 
    341348