#6767 closed (fixed)
Oracle backend treats DecimalFields as floats somewhere, losing precision
Reported by: | Matt Boersma | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Keywords: | oracle Oracle decimal float precision | |
Cc: | Erin Kelly, richard.davies@… | Triage Stage: | Accepted |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
This is really a cx_Oracle limitation. One workaround Ian Kelly and I discussed is to set cursor.numbersAsStrings = True and then Do The Right Thing in converting from strings. (The cx_Oracle rowfactory callback mechanism could be a nice way to handle this.)
>>> from foo.models import Address >>> from decimal import Decimal >>> a = Address(latitude=Decimal('-105.2513837814331')) >>> a.save() >>> a.latitude Decimal("-105.2513837814331") >>> b = Address.objects.get(pk=a.pk) >>> b.latitude Decimal("-105.25138378143301000000000000000000000")
Nothing in the test suite catches this! We need to add a test for this before creating the fix.
Attachments (2)
Change History (15)
comment:1 by , 17 years ago
comment:2 by , 17 years ago
Cc: | added; removed |
---|
comment:3 by , 17 years ago
#5079 has patch which includes a similar test. There's also a fix in there, which may or may not resolve your issue :) If you have an oracle DB handy and want to run the tests on it be my guest.
comment:4 by , 17 years ago
As I commented at #5079, the patch there doesn't fix this problem, so there may actually be multiple bugs where Oracle is concerned.
comment:5 by , 16 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:6 by , 16 years ago
Cc: | added |
---|
For reference in case it is useful, I had a similar problem specifically with the MySQL backend, and have fixed my issue with a patch to MySQLdb
http://sourceforge.net/tracker/index.php?func=detail&aid=2051833&group_id=22307&atid=374934
comment:7 by , 16 years ago
This problem (for Oracle) relates to retrieval of the data, whereas #5079 relates to storing it in the first place.
In #5079 the data goes through a float conversion before being sent to the DB in a string format. As a result the actual values stored in the database may be incorrect due to float limitations. Testing (via attempting to save Decimal("0.7") shows #5079 currently affects MySQL, PostgreSQL, and Oracle, but not sqlite.
Fixing #5079 does not fix this problem for Oracle, because when retrieving the data from Oracle it is coming back from the DB as a float. You can see that by using raw sql queries under a manage.py shell -- the type for the data in the tuples returned is float. However if you look at the data using the Oracle web browser, you can see it has been stored in the DB correctly (when #5079 is fixed), it is just getting mangled on retrieval. I don't know how to get the db connector to use Decimals instead of floats for retrieval of this kind of data.
sqlite (as noted above) also fails on this case. But the reason is different. sqlite doesn't suffer from #5079, exactly, but maybe a variant. It seems that any values stores are rounded to 15 significant digits. So if I store:
Decimal(".251383781433984125") I see: 0.251383781433984 via sqlite3 shell and I get back the same as a Decimal on a fetch.
It is rounding, not truncating, since if I change the ending "4125" to "4525" the stored value ends in 5 instead of 4.
I'll hopefully fix #5079 soon, this ticket should probably be used to fix the fact that the Decimals coming back from Oracle come in as floats (I have no idea how to do that) and we probably need a 3rd ticket (if there isn't already one) for Decimals getting rounded to 15 digits for storage on sqlite. I also have no idea where that is happening.
by , 16 years ago
Prevents float conversions by pulling in numeric data as strings and casting them by hand. Testing is covered by the regression test for #5079.
by , 16 years ago
Attachment: | 6767-2.diff added |
---|
Patch that doesn't remove field-based number formatting of decimal objects.
comment:8 by , 16 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
comment:9 by , 16 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Decimal module doesn't exist in Python 2.3. Use Django's decimal module as fallback.
-from decimal import Decimal +try: + from decimal import Decimal +except ImportError: + from django.utils import _decimal as Decimal
comment:10 by , 16 years ago
Sorry, this way:
-from decimal import Decimal +try: + from decimal import Decimal +except ImportError: + from django.utils._decimal import Decimal
comment:11 by , 16 years ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
Apparently we get a similar failure in the sqlite3 backend. I would assume this is considered an error there as well, but if this is somehow expected behavior please add a comment here to that effect.