Opened 5 years ago

Closed 3 years ago

#17601 closed Cleanup/optimization (fixed)

Error code from database exception should not be lost during exception handling (psycopg2)

Reported by: Piotr Czachur Owned by: James Aylett
Component: Database layer (models, ORM) Version: master
Severity: Normal Keywords:
Cc: james@… Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


Here is a fragment from source:django/trunk/django/db/backends/postgresql_psycopg2/

    def execute(self, query, args=None):
            return self.cursor.execute(query, args)
        except Database.IntegrityError, e:
            raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
        except Database.DatabaseError, e:
            raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]

In case of Psycopg2 and database exceptions, error code is set as psycopg2 exception propery called pgcode instead of being passed as exception argument, so this way we're loosing info about "pgcode" which is valuable. Handling database exception by parsing error message is a bit awkward.

I'm not sure if other backends are affected by this issue, but I can investigate and provide a patch if we agree on this issue.

I've just filled a bug in this subject against Psycopg2:
If they fix it, this ticket will become "worksforme".

Change History (9)

comment:1 Changed 5 years ago by Claude Paroz

Needs documentation: unset
Needs tests: unset
Patch needs improvement: unset
Triage Stage: UnreviewedAccepted
Type: UncategorizedCleanup/optimization

Sure, all value-added information we can bring might be useful for the poor debugging dev :-)

comment:2 Changed 3 years ago by James Aylett

The bug filed against psycopg2 was closed, rightly. I think what is needed (within Django) is a way of getting at the original exception given the common wrapper, so that (in this case) it's possible to access pgcode and particularly diag from the psycopg2 exception. In python 3 this is available via __cause__ (AIUI; I haven't actually used py3 in anger). Could we just set __cause__ whether we're running under py3 or not? Something like (at [c36b75c814f068fcb722d46bd5e71cbaddf9bf2d]):

--- a/django/db/
+++ b/django/db/
@@ -91,8 +91,7 @@ class DatabaseErrorWrapper(object):
                 except AttributeError:
                     args = (exc_value,)
                 dj_exc_value = dj_exc_type(*args)
-                if six.PY3:
-                    dj_exc_value.__cause__ = exc_value
+                dj_exc_value.__cause__ = exc_value
                 # Only set the 'errors_occurred' flag for errors that may make
                 # the connection unusable.
                 if dj_exc_type not in (DataError, IntegrityError):

comment:3 Changed 3 years ago by James Aylett

Cc: james@… added

comment:4 Changed 3 years ago by Aymeric Augustin

I suppose setting cause unconditionally can't hurt...

comment:5 Changed 3 years ago by James Aylett

I can't think of a way it would, since it doesn't exist at the moment. (It's possible that poorly-written code might use it to assume it's running under py3, but that seems a stretch.)

I'm not quite sure where we'd want to document this; probably a small note in ref/exceptions?. (There are no tests that use __cause__ as far as I can tell, so I don't know if we need an explicit test case for this.)

comment:6 Changed 3 years ago by James Aylett

Owner: changed from nobody to James Aylett
Status: newassigned

comment:7 Changed 3 years ago by James Aylett

Pull request 1241 ( rather than messing around with tiny patches in comments.

comment:8 Changed 3 years ago by James Aylett

Has patch: set

comment:9 Changed 3 years ago by James Aylett <james@…>

Resolution: fixed
Status: assignedclosed

In 54485557855d58d9a5027511025d5ab22f721c6d:

Fixed #17601 -- expose underlying db exceptions under py2

Use cause to expose the underlying database exceptions even
under python 2.

Note: See TracTickets for help on using tickets.
Back to Top