#28263: TestCase breaks for databases that don't support savepoints
-----------------------------------+------------------------------------
     Reporter:  Lokesh Dokara      |                    Owner:  nobody
         Type:  Bug                |                   Status:  new
    Component:  Testing framework  |                  Version:  1.11
     Severity:  Normal             |               Resolution:
     Keywords:                     |             Triage Stage:  Accepted
    Has patch:  0                  |      Needs documentation:  0
  Needs tests:  0                  |  Patch needs improvement:  0
Easy pickings:  0                  |                    UI/UX:  0
-----------------------------------+------------------------------------

Comment (by Héctor Pablos):

 Bringing this up again because I'm facing the same issue Lokesh Dokara was
 facing in django 2.2.7. In my case I'm developing a database backend for
 [https://www.exasol.com/en/ / ExaSol].

 I also met the problem while executing some tests, and I managed to
 overcome it by extending {{{django.test.testcases.TestCase}} and
 overwriting the {{{_enter_atomics}}} and {{{_rollback_atomics}}} methods
 so no transactions are started or rolled back for databases with
 {{{uses_savepoints = False}}} and {{{supports_transactions = True}}}, but
 that means I have to be careful not to modify the data, as it won't be
 rolled back. Using {{{TransactionTestCase}}}, which would not create any
 transactions, is not an option for me, as I can't truncate other databases
 the tests depend on.

 The underlying issue is not only in the tests, every database backend with
 {{{uses_savepoints = False}}} and {{{supports_transactions = True}}} will
 raise exceptions in the following scenario:

 {{{
 - Init parent transaction (django.db.transaction.Atomic.__enter__)
     * Not in an atomic block, so:
        connection.commit_on_exit = True
        connection.needs_rollback = False
        connection.set_autocommit(False,
 force_begin_transaction_with_broken_autocommit=True)
        connection.in_atomic_block = True

     - Init child transaction 1 (django.db.transaction.Atomic.__enter__)
         * In an atomic block, so:
            call to connection.savepoint(), as
 connection.features.uses_savepoints == False, returns None
            connection.savepoint_ids.append(None)

     - Do some database operation that raises an exception and marks the
 connection as needs_rollback = True

     - Exit child transaction 1 (django.db.transaction.Atomic.__exit__)
            * sid = connection.savepoint_ids.pop(), so
            connection.savepoint_ids == []
            sid == None
            Connection is inside an atomic block and sid is None so
 connection.needs_rollback is set to True again and nothing else is done.

     - Init child transaction 2 (django.db.transaction.Atomic.__enter__)
         * Same as init child transaction 1

     - Execute a query (django.db.backends.utils.CursorWrapper._execute)
           We get to django.db.backends.utils.CursorWrapper._execute, where
 self.db.validate_no_broken_transaction() is executed.
           This function finds connection.needs_rollback == True, because
 it was set when exiting the child transaction 1, and
           it raises the aforementioned TransactionManagementError
 }}}

 I'm afraid I'm not really sure how to fix this, but it's not something
 just affecting tests. The {{{django.db.transaction.Atomic}}} class doesn't
 seem to be able to handle the databases without savepoints portrayed here.

 The normal flow would be to just do nothing when any child transaction is
 started or exited in these databases, and leave it up to the topmost
 transaction to commit or rollback at the end and set autocommit again to
 True, but of course that would mean that users trying to create
 subtransactions and roll back only those changes would not be able to do
 so, and all the changes would be rolled back silently.

 Let me know if this sounds like something reasonable and I could try to do
 a PR myself, I'm just afraid I don't have the necessary whole context to
 do so.

 Cheers!

-- 
Ticket URL: <https://code.djangoproject.com/ticket/28263#comment:7>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/066.ee0e19f7160b923231984387b6890906%40djangoproject.com.

Reply via email to