Opened 8 years ago
Last modified 7 years ago
#27979 closed Cleanup/optimization
Bug: Using F() with mysql can leak an unwrapped OperationalError — at Version 1
Reported by: | Chris Dary | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.10 |
Severity: | Normal | Keywords: | db, mysql, exceptions, OperationalError |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Hey folks - I'm using MySQL 5.7, python 3.6, Django 1.10.
I just ran into this one: It looks like with a PositiveIntegerField it's possible to get django to leak a raw OperationalError from mysql rather than a wrapped one as it's intended if you try to set it below zero. I assume this is a specific case of a more general issue.
Here's a sample model:
from django.db import models class SampleModel(models.Model): num = models.PositiveIntegerField()
And here's some example code showing it leaking a mysql exception which is not trappable by the traditional django.db exceptions (in shell_plus to show the queries).
In [1]: import sys ...: from django.db import models ...: In [2]: sample = SampleModel.objects.create(num=0) ...: INSERT INTO `users_samplemodel` (`num`) VALUES (0) Execution time: 0.000492s [Database: default] In [3]: try: ...: sample.num = models.F('num') - 1 ...: sample.save() ...: except: ...: err_type, error, traceback = sys.exc_info() ...: print("Caught Exception of type: %s" % err_type) ...: print("Error: %s" % error) ...: UPDATE `users_samplemodel` SET `num` = (`users_samplemodel`.`num` - 1) WHERE `users_samplemodel`.`id` = 2 Execution time: 0.000409s [Database: default] Caught Exception of type: <class '_mysql_exceptions.OperationalError'> Error: (1690, "BIGINT UNSIGNED value is out of range in '(`sample`.`users_samplemodel`.`num` - 1)'")
Looks like this just needs to be trapped and wrapped somewhere?
I was able to work around it just by using a transaction and checking exists() first, but thought it'd be helpful to report.
Thanks for all your work on Django!