#33160: postgresql DatabaseWrapper._nodb_cursor causes confusing/wrong "Django 
was
unable to create a connection to the 'postgres' database" warning in case
of errors from queries
-------------------------------------+-------------------------------------
     Reporter:  Daniel Hahler        |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  Database layer       |                  Version:  3.2
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Description changed by Daniel Hahler:

Old description:

> {{_nodb_cursor}} might cause a confusing/wrong warning, e.g. via
> {{teardown_database}}:
>
> {{{
>   [35] …/Vcs/django/django/test/utils.py(313)teardown_databases()
>        connection.creation.destroy_test_db(old_name, verbosity, keepdb)
>   [36]
> …/Vcs/django/django/db/backends/base/creation.py(282)destroy_test_db()
>        self._destroy_test_db(test_database_name, verbosity)
>   [37]
> …/Vcs/django/django/db/backends/base/creation.py(298)_destroy_test_db()
>        cursor.execute("DROP DATABASE %s"
>   [38] /usr/lib/python3.9/contextlib.py(137)__exit__()
>        self.gen.throw(typ, value, traceback)
> > [39]
> …/Vcs/django/django/db/backends/postgresql/base.py(306)_nodb_cursor()
>        warnings.warn(
> (Pdb++) l
> 301                 with super()._nodb_cursor() as cursor:
> 302                     yield cursor
> 303             except (Database.DatabaseError, WrappedDatabaseError) as
> exc:
> 304                 e = exc
> 305                 __import__('pdb').set_trace()
> 306  ->             warnings.warn(
> 307                     "Normally Django will use a connection to the
> 'postgres' database "
> 308                     "to avoid running initialization queries against
> the production "
> 309                     "database when it's not needed (for example, when
> running tests). "
> 310                     "Django was unable to create a connection to the
> 'postgres' database "
> 311                     "and will use the first PostgreSQL database
> instead.",
> (Pdb++) e
> OperationalError('database "test_foo" is being accessed by other
> users\nDETAIL:  There is 1 other session using the database.\n')
> }}}
>
> As you can see there is another issue this will (partly) swallow/hide
> then, apart from the message being just wrong: it is not the connection
> that failed, but a query in there:
>
> {{{
> [37] >
> …/django/django/db/backends/base/creation.py(298)_destroy_test_db(), 4
> frames hidden
>
> 289         def _destroy_test_db(self, test_database_name, verbosity):
> 290             """
> 291             Internal implementation - remove the test db tables.
> 292             """
> 293             # Remove the test database to clean up after
> 294             # ourselves. Connect to the previous database (not the
> test database)
> 295             # to do so, because it's not allowed to delete a database
> while being
> 296             # connected to it.
> 297             with self._nodb_cursor() as cursor:
> 298  ->             cursor.execute("DROP DATABASE %s"
> 299                                %
> self.connection.ops.quote_name(test_database_name))
> }}}
>
> Only {{connect()} should get wrapped/handled in {{_nodb_cursor}} probably
> (in terms of adding the warning), which could be achieved by handling
> specific exceptions only, or re-raising it in case a {{cursor}} was used
> (i.e. it could connect).

New description:

 {{_nodb_cursor}} might cause a confusing/wrong warning, e.g. via
 {{teardown_database}}:

 {{{
   [35] …/Vcs/django/django/test/utils.py(313)teardown_databases()
        connection.creation.destroy_test_db(old_name, verbosity, keepdb)
   [36]
 …/Vcs/django/django/db/backends/base/creation.py(282)destroy_test_db()
        self._destroy_test_db(test_database_name, verbosity)
   [37]
 …/Vcs/django/django/db/backends/base/creation.py(298)_destroy_test_db()
        cursor.execute("DROP DATABASE %s"
   [38] /usr/lib/python3.9/contextlib.py(137)__exit__()
        self.gen.throw(typ, value, traceback)
 > [39]
 …/Vcs/django/django/db/backends/postgresql/base.py(306)_nodb_cursor()
        warnings.warn(
 (Pdb++) l
 301                 with super()._nodb_cursor() as cursor:
 302                     yield cursor
 303             except (Database.DatabaseError, WrappedDatabaseError) as
 exc:
 304                 e = exc
 305                 __import__('pdb').set_trace()
 306  ->             warnings.warn(
 307                     "Normally Django will use a connection to the
 'postgres' database "
 308                     "to avoid running initialization queries against
 the production "
 309                     "database when it's not needed (for example, when
 running tests). "
 310                     "Django was unable to create a connection to the
 'postgres' database "
 311                     "and will use the first PostgreSQL database
 instead.",
 (Pdb++) e
 OperationalError('database "test_foo" is being accessed by other
 users\nDETAIL:  There is 1 other session using the database.\n')
 }}}

 As you can see there is another issue this will (partly) swallow/hide
 then, apart from the message being just wrong: it is not the connection
 that failed, but a query in there:

 {{{
 [37] >
 …/django/django/db/backends/base/creation.py(298)_destroy_test_db(), 4
 frames hidden

 289         def _destroy_test_db(self, test_database_name, verbosity):
 290             """
 291             Internal implementation - remove the test db tables.
 292             """
 293             # Remove the test database to clean up after
 294             # ourselves. Connect to the previous database (not the
 test database)
 295             # to do so, because it's not allowed to delete a database
 while being
 296             # connected to it.
 297             with self._nodb_cursor() as cursor:
 298  ->             cursor.execute("DROP DATABASE %s"
 299                                %
 self.connection.ops.quote_name(test_database_name))
 }}}

 Only {{connect()} should get wrapped/handled in {{_nodb_cursor}} probably
 (in terms of adding the warning), which could be achieved by handling
 specific exceptions only, or re-raising it in case a {{cursor}} was used
 (i.e. it could connect).

 Possible fix: https://github.com/django/django/pull/14918

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/33160#comment:2>
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/065.c6c9de466c9be9d58cc7d9e75793805d%40djangoproject.com.

Reply via email to