From c6a4f853c7167c1001761dcff30d7a64690e8236 Mon Sep 17 00:00:00 2001 From: Alex Fischer Date: Wed, 28 Aug 2024 09:53:40 -0600 Subject: [PATCH] Fixed #35712 -- Prevented Q.check() from leaving the connection in an unusable state. Co-authored-by: Simon Charette --- django/db/models/query_utils.py | 14 +++++++++++--- tests/queries/test_q.py | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 1bf396723e..096395e1b8 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -10,9 +10,10 @@ import functools import inspect import logging from collections import namedtuple +from contextlib import nullcontext from django.core.exceptions import FieldError -from django.db import DEFAULT_DB_ALIAS, DatabaseError, connections +from django.db import DEFAULT_DB_ALIAS, DatabaseError, connections, transaction from django.db.models.constants import LOOKUP_SEP from django.utils import tree from django.utils.functional import cached_property @@ -130,14 +131,21 @@ class Q(tree.Node): value = Value(value) query.add_annotation(value, name, select=False) query.add_annotation(Value(1), "_check") + connection = connections[using] # This will raise a FieldError if a field is missing in "against". - if connections[using].features.supports_comparing_boolean_expr: + if connection.features.supports_comparing_boolean_expr: query.add_q(Q(Coalesce(self, True, output_field=BooleanField()))) else: query.add_q(self) compiler = query.get_compiler(using=using) + context_manager = ( + transaction.atomic(using=using) + if connection.in_atomic_block + else nullcontext() + ) try: - return compiler.execute_sql(SINGLE) is not None + with context_manager: + return compiler.execute_sql(SINGLE) is not None except DatabaseError as e: logger.warning("Got a database error calling check() on %r: %s", self, e) return True diff --git a/tests/queries/test_q.py b/tests/queries/test_q.py index f7192a430a..f37d7becac 100644 --- a/tests/queries/test_q.py +++ b/tests/queries/test_q.py @@ -1,4 +1,5 @@ from django.core.exceptions import FieldError +from django.db import connection from django.db.models import ( BooleanField, Exists, @@ -327,3 +328,6 @@ class QCheckTests(TestCase): f"Got a database error calling check() on {q!r}: ", cm.records[0].getMessage(), ) + + # We must leave the connection in a usable state (#35712). + self.assertTrue(connection.is_usable())