Пример #1
0
 def test_executes_only_after_final_transaction_committed(self):
     with transaction.atomic():
         with transaction.atomic():
             self.do(1)
             self.assertNotified([])
         self.assertNotified([])
     self.assertDone([1])
Пример #2
0
    def test_hooks_cleared_after_successful_commit(self):
        with transaction.atomic():
            self.do(1)
        with transaction.atomic():
            self.do(2)

        self.assertDone([1, 2])  # not [1, 1, 2]
Пример #3
0
    def test_implicit_savepoint_rollback(self):
        """MySQL implicitly rolls back savepoints when it deadlocks (#22291)."""
        Reporter.objects.create(id=1)
        Reporter.objects.create(id=2)

        main_thread_ready = threading.Event()

        def other_thread():
            try:
                with transaction.atomic():
                    Reporter.objects.select_for_update().get(id=1)
                    main_thread_ready.wait()
                    # 1) This line locks... (see below for 2)
                    Reporter.objects.exclude(id=1).update(id=2)
            finally:
                # This is the thread-local connection, not the main connection.
                connection.close()

        other_thread = threading.Thread(target=other_thread)
        other_thread.start()

        with self.assertRaisesMessage(OperationalError, 'Deadlock found'):
            # Double atomic to enter a transaction and create a savepoint.
            with transaction.atomic():
                with transaction.atomic():
                    Reporter.objects.select_for_update().get(id=2)
                    main_thread_ready.set()
                    # The two threads can't be synchronized with an event here
                    # because the other thread locks. Sleep for a little while.
                    time.sleep(1)
                    # 2) ... and this line deadlocks. (see above for 1)
                    Reporter.objects.exclude(id=2).update(id=1)

        other_thread.join()
Пример #4
0
 def test_merged_rollback_commit(self):
     with self.assertRaisesMessage(Exception, "Oops"):
         with transaction.atomic():
             Reporter.objects.create(last_name="Tintin")
             with transaction.atomic(savepoint=False):
                 Reporter.objects.create(last_name="Haddock")
             raise Exception("Oops, that's his first name")
     self.assertQuerysetEqual(Reporter.objects.all(), [])
Пример #5
0
    def test_wrap_callable_instance(self):
        """#20028 -- Atomic must support wrapping callable instances."""
        class Callable:
            def __call__(self):
                pass

        # Must not raise an exception
        transaction.atomic(Callable())
Пример #6
0
    def test_runs_hooks_in_order_registered(self):
        with transaction.atomic():
            self.do(1)
            with transaction.atomic():
                self.do(2)
            self.do(3)

        self.assertDone([1, 2, 3])
Пример #7
0
 def test_merged_commit_rollback(self):
     with transaction.atomic():
         Reporter.objects.create(first_name="Tintin")
         with self.assertRaisesMessage(Exception, "Oops"):
             with transaction.atomic(savepoint=False):
                 Reporter.objects.create(first_name="Haddock")
                 raise Exception("Oops, that's his last name")
     # Writes in the outer block are rolled back too.
     self.assertQuerysetEqual(Reporter.objects.all(), [])
Пример #8
0
 def test_nested_commit_commit(self):
     with transaction.atomic():
         Reporter.objects.create(first_name="Tintin")
         with transaction.atomic():
             Reporter.objects.create(first_name="Archibald",
                                     last_name="Haddock")
     self.assertQuerysetEqual(
         Reporter.objects.all(),
         ['<Reporter: Archibald Haddock>', '<Reporter: Tintin>'])
Пример #9
0
 def test_nested_commit_rollback(self):
     with transaction.atomic():
         Reporter.objects.create(first_name="Tintin")
         with self.assertRaisesMessage(Exception, "Oops"):
             with transaction.atomic():
                 Reporter.objects.create(first_name="Haddock")
                 raise Exception("Oops, that's his last name")
     self.assertQuerysetEqual(Reporter.objects.all(),
                              ['<Reporter: Tintin>'])
Пример #10
0
    def test_no_savepoints_atomic_merged_with_outer(self):
        with transaction.atomic():
            with transaction.atomic():
                self.do(1)
                try:
                    with transaction.atomic(savepoint=False):
                        raise ForcedError()
                except ForcedError:
                    pass

        self.assertDone([])
Пример #11
0
    def test_inner_savepoint_does_not_affect_outer(self):
        with transaction.atomic():
            with transaction.atomic():
                self.do(1)
                try:
                    with transaction.atomic():
                        raise ForcedError()
                except ForcedError:
                    pass

        self.assertDone([1])
Пример #12
0
    def test_error_in_hook_doesnt_prevent_clearing_hooks(self):
        try:
            with transaction.atomic():
                transaction.on_commit(lambda: self.notify('error'))
        except ForcedError:
            pass

        with transaction.atomic():
            self.do(1)

        self.assertDone([1])
Пример #13
0
    def test_hooks_cleared_on_reconnect(self):
        with transaction.atomic():
            self.do(1)
            connection.close()

        connection.connect()

        with transaction.atomic():
            self.do(2)

        self.assertDone([2])
Пример #14
0
    def test_no_hooks_run_from_failed_transaction(self):
        """If outer transaction fails, no hooks from within it run."""
        try:
            with transaction.atomic():
                with transaction.atomic():
                    self.do(1)
                raise ForcedError()
        except ForcedError:
            pass

        self.assertDone([])
Пример #15
0
    def test_inner_savepoint_rolled_back_with_outer(self):
        with transaction.atomic():
            try:
                with transaction.atomic():
                    with transaction.atomic():
                        self.do(1)
                    raise ForcedError()
            except ForcedError:
                pass
            self.do(2)

        self.assertDone([2])
Пример #16
0
    def test_hooks_cleared_after_rollback(self):
        try:
            with transaction.atomic():
                self.do(1)
                raise ForcedError()
        except ForcedError:
            pass

        with transaction.atomic():
            self.do(2)

        self.assertDone([2])
Пример #17
0
        def add(self, *objs, bulk=True):
            self._remove_prefetched_objects()
            db = router.db_for_write(self.model, instance=self.instance)

            def check_and_update_obj(obj):
                if not isinstance(obj, self.model):
                    raise TypeError("'%s' instance expected, got %r" %
                                    (self.model._meta.object_name, obj))
                setattr(obj, self.content_type_field_name, self.content_type)
                setattr(obj, self.object_id_field_name, self.pk_val)

            if bulk:
                pks = []
                for obj in objs:
                    if obj._state.adding or obj._state.db != db:
                        raise ValueError(
                            "%r instance isn't saved. Use bulk=False or save "
                            "the object first." % obj)
                    check_and_update_obj(obj)
                    pks.append(obj.pk)

                self.model._base_manager.using(db).filter(pk__in=pks).update(
                    **{
                        self.content_type_field_name: self.content_type,
                        self.object_id_field_name: self.pk_val,
                    })
            else:
                with transaction.atomic(using=db, savepoint=False):
                    for obj in objs:
                        check_and_update_obj(obj)
                        obj.save()
Пример #18
0
    def test_db_query_in_hook(self):
        with transaction.atomic():
            Thing.objects.create(num=1)
            transaction.on_commit(
                lambda: [self.notify(t.num) for t in Thing.objects.all()])

        self.assertDone([1])
Пример #19
0
 def _enter_atomics(cls):
     """Open atomic blocks for multiple databases."""
     atomics = {}
     for db_name in cls._databases_names():
         atomics[db_name] = transaction.atomic(using=db_name)
         atomics[db_name].__enter__()
     return atomics
Пример #20
0
    def test_add(self):
        # Create an Article via the Reporter object.
        new_article = self.r.article_set.create(headline="John's second story", pub_date=datetime.date(2005, 7, 29))
        self.assertEqual(repr(new_article), "<Article: John's second story>")
        self.assertEqual(new_article.reporter.id, self.r.id)

        # Create a new article, and add it to the article set.
        new_article2 = Article(headline="Paul's story", pub_date=datetime.date(2006, 1, 17))
        msg = "<Article: Paul's story> instance isn't saved. Use bulk=False or save the object first."
        with self.assertRaisesMessage(ValueError, msg):
            self.r.article_set.add(new_article2)

        self.r.article_set.add(new_article2, bulk=False)
        self.assertEqual(new_article2.reporter.id, self.r.id)
        self.assertQuerysetEqual(
            self.r.article_set.all(),
            ["<Article: John's second story>", "<Article: Paul's story>", "<Article: This is a test>"]
        )

        # Add the same article to a different article set - check that it moves.
        self.r2.article_set.add(new_article2)
        self.assertEqual(new_article2.reporter.id, self.r2.id)
        self.assertQuerysetEqual(self.r2.article_set.all(), ["<Article: Paul's story>"])

        # Adding an object of the wrong type raises TypeError.
        with transaction.atomic():
            with self.assertRaisesMessage(TypeError, "'Article' instance expected, got <Reporter:"):
                self.r.article_set.add(self.r2)
        self.assertQuerysetEqual(
            self.r.article_set.all(),
            ["<Article: John's second story>", "<Article: This is a test>"]
        )
Пример #21
0
    def _rename(self, apps, schema_editor, old_model, new_model):
        ContentType = apps.get_model('contenttypes', 'ContentType')
        db = schema_editor.connection.alias
        if not router.allow_migrate_model(db, ContentType):
            return

        try:
            content_type = ContentType.objects.db_manager(
                db).get_by_natural_key(self.app_label, old_model)
        except ContentType.DoesNotExist:
            pass
        else:
            content_type.model = new_model
            try:
                with transaction.atomic(using=db):
                    content_type.save(update_fields={'model'})
            except IntegrityError:
                # Gracefully fallback if a stale content type causes a
                # conflict as remove_stale_contenttypes will take care of
                # asking the user what should be done next.
                content_type.model = old_model
            else:
                # Clear the cache as the `get_by_natual_key()` call will cache
                # the renamed ContentType instance by its old model name.
                ContentType.objects.clear_cache()
Пример #22
0
 def test_prevent_rollback(self):
     with transaction.atomic():
         Reporter.objects.create(first_name="Tintin")
         sid = transaction.savepoint()
         # trigger a database error inside an inner atomic without savepoint
         with self.assertRaises(DatabaseError):
             with transaction.atomic(savepoint=False):
                 with connection.cursor() as cursor:
                     cursor.execute(
                         "SELECT no_such_col FROM transactions_reporter")
         # prevent atomic from rolling back since we're recovering manually
         self.assertTrue(transaction.get_rollback())
         transaction.set_rollback(False)
         transaction.savepoint_rollback(sid)
     self.assertQuerysetEqual(Reporter.objects.all(),
                              ['<Reporter: Tintin>'])
Пример #23
0
 def test_postgres_options(self):
     qs = Tag.objects.filter(name='test')
     test_options = [
         {
             'COSTS': False,
             'BUFFERS': True,
             'ANALYZE': True
         },
         {
             'costs': False,
             'buffers': True,
             'analyze': True
         },
         {
             'verbose': True,
             'timing': True,
             'analyze': True
         },
         {
             'verbose': False,
             'timing': False,
             'analyze': True
         },
     ]
     if connection.pg_version >= 100000:
         test_options.append({'summary': True})
     for options in test_options:
         with self.subTest(**options), transaction.atomic():
             with CaptureQueriesContext(connection) as captured_queries:
                 qs.explain(format='text', **options)
             self.assertEqual(len(captured_queries), 1)
             for name, value in options.items():
                 option = '{} {}'.format(name.upper(),
                                         'true' if value else 'false')
                 self.assertIn(option, captured_queries[0]['sql'])
Пример #24
0
 def test_force_rollback(self):
     with transaction.atomic():
         Reporter.objects.create(first_name="Tintin")
         # atomic block shouldn't rollback, but force it.
         self.assertFalse(transaction.get_rollback())
         transaction.set_rollback(True)
     self.assertQuerysetEqual(Reporter.objects.all(), [])
Пример #25
0
    def delete(self):
        # sort instance collections
        for model, instances in self.data.items():
            self.data[model] = sorted(instances, key=attrgetter("pk"))

        # if possible, bring the models in an order suitable for databases that
        # don't support transactions or cannot defer constraint checks until the
        # end of a transaction.
        self.sort()
        # number of objects deleted for each model label
        deleted_counter = Counter()

        with transaction.atomic(using=self.using, savepoint=False):
            # send pre_delete signals
            for model, obj in self.instances_with_model():
                if not model._meta.auto_created:
                    signals.pre_delete.send(sender=model,
                                            instance=obj,
                                            using=self.using)

            # fast deletes
            for qs in self.fast_deletes:
                count = qs._raw_delete(using=self.using)
                deleted_counter[qs.model._meta.label] += count

            # update fields
            for model, instances_for_fieldvalues in self.field_updates.items():
                for (field,
                     value), instances in instances_for_fieldvalues.items():
                    query = sql.UpdateQuery(model)
                    query.update_batch([obj.pk for obj in instances],
                                       {field.name: value}, self.using)

            # reverse instance collections
            for instances in self.data.values():
                instances.reverse()

            # delete instances
            for model, instances in self.data.items():
                query = sql.DeleteQuery(model)
                pk_list = [obj.pk for obj in instances]
                count = query.delete_batch(pk_list, self.using)
                deleted_counter[model._meta.label] += count

                if not model._meta.auto_created:
                    for obj in instances:
                        signals.post_delete.send(sender=model,
                                                 instance=obj,
                                                 using=self.using)

        # update collected instances
        for instances_for_fieldvalues in self.field_updates.values():
            for (field, value), instances in instances_for_fieldvalues.items():
                for obj in instances:
                    setattr(obj, field.attname, value)
        for model, instances in self.data.items():
            for instance in instances:
                setattr(instance, model._meta.pk.attname, None)
        return sum(deleted_counter.values()), dict(deleted_counter)
Пример #26
0
 def test_for_update_of_self_when_self_is_not_selected(self):
     """
     select_for_update(of=['self']) when the only columns selected are from
     related tables.
     """
     with transaction.atomic():
         values = list(Person.objects.select_related('born').select_for_update(of=('self',)).values('born__name'))
     self.assertEqual(values, [{'born__name': self.city1.name}])
Пример #27
0
 def test_unsupported_nowait_raises_error(self):
     """
     NotSupportedError is raised if a SELECT...FOR UPDATE NOWAIT is run on
     a database backend that supports FOR UPDATE but not NOWAIT.
     """
     with self.assertRaisesMessage(NotSupportedError, 'NOWAIT is not supported on this database backend.'):
         with transaction.atomic():
             Person.objects.select_for_update(nowait=True).get()
Пример #28
0
 def test_unsupported_skip_locked_raises_error(self):
     """
     NotSupportedError is raised if a SELECT...FOR UPDATE SKIP LOCKED is run
     on a database backend that supports FOR UPDATE but not SKIP LOCKED.
     """
     with self.assertRaisesMessage(NotSupportedError, 'SKIP LOCKED is not supported on this database backend.'):
         with transaction.atomic():
             Person.objects.select_for_update(skip_locked=True).get()
Пример #29
0
 def test_ordered_select_for_update(self):
     """
     Subqueries should respect ordering as an ORDER BY clause may be useful
     to specify a row locking order to prevent deadlocks (#27193).
     """
     with transaction.atomic():
         qs = Person.objects.filter(id__in=Person.objects.order_by('-id').select_for_update())
         self.assertIn('ORDER BY', str(qs.query))
Пример #30
0
 def test_for_update_sql_generated_skip_locked(self):
     """
     The backend's FOR UPDATE SKIP LOCKED variant appears in
     generated SQL when select_for_update is invoked.
     """
     with transaction.atomic(), CaptureQueriesContext(connection) as ctx:
         list(Person.objects.all().select_for_update(skip_locked=True))
     self.assertTrue(self.has_for_update_sql(ctx.captured_queries, skip_locked=True))