Opened 5 years ago

Closed 4 years ago

#1085 closed defect (fixed)

Double Precision Floats are not properly represented with SA

Reported by: guest Owned by: zzzeek
Priority: medium Milestone: 0.6.0
Component: postgres Severity: minor - half an hour
Keywords: Cc:
Progress State: in progress

Description

DPs are implemented as Float(precision=53) which, last time I checked DPs were 64 bits in postgres. I have attached a patch.

-chris perkins
(percious)

Attachments (1)

postgres.double.precision.fix.percious.diff (1.7 KB) - added by guest 5 years ago.

Download all attachments as: .zip

Change History (14)

comment:1 follow-up: Changed 5 years ago by zzzeek

  • Milestone set to 0.5.0
  • Severity changed from no triage selected yet to minor - half an hour

why'd you comment out Numeric and Float in the type map ?

comment:2 in reply to: ↑ 1 Changed 5 years ago by guest

Replying to zzzeek:

why'd you comment out Numeric and Float in the type map ?

Because they cause double precision numbers to come in as floats when a database is reflected.

comment:3 Changed 5 years ago by zzzeek

in the 'colspecs' map. that map is used when a Table uses a Float or Numeric, which must be converted to PGFloat or PGNumeric during table create as well as bind parameter / result processing. They're not used during reflection as far as I can remember. I would think that some tests would fail with those lines commented out.

comment:4 Changed 5 years ago by zzzeek

indeed, three failures in test/sql/testtypes.py. Also what is the actual symptom you're having an issue with, trying to reflect a table then issue a CREATE TABLE again ? the "53" is only used within reflection and is then no longer used unless a CREATE TABLE is issued. full round trips like that are very difficult to achieve since we don't reflect every single type.

comment:5 Changed 5 years ago by guest

The precision is used when you are checking attributes when comparing a reflected schema to one that you have hand-written.

Also, the reflection gets the wrong type: PGFloat which is very much different from a PGDoublePrecision. As I said before, the max resolution on a PGFloat is 53 bits, where double precision is 64 bits. Obviously my patch needs more work before it is viable...

comment:6 Changed 5 years ago by zzzeek

I really think the patch should work as is just without the Numeric and Float entries in the colspecs dictionary commented out. The reflection process does not consult this dictionary at all.

comment:7 Changed 5 years ago by zzzeek

  • Milestone changed from 0.5.0 to 0.5.xx
  • Progress State changed from awaiting triage to needs questions answered

need status on this. This is the proposed patch:

Index: lib/sqlalchemy/databases/postgres.py
===================================================================
--- lib/sqlalchemy/databases/postgres.py	(revision 4873)
+++ lib/sqlalchemy/databases/postgres.py	(working copy)
@@ -119,6 +119,13 @@
     def get_col_spec(self):
         return "BOOLEAN"
 
+class PGDoublePrecision(sqltypes.Numeric):
+    def __init__(self, asdecimal=True):
+        self.asdecimal=asdecimal
+    
+    def get_col_spec(self):
+        return "DOUBLE PRECISION"
+
 class PGArray(sqltypes.MutableType, sqltypes.Concatenable, sqltypes.TypeEngine):
     def __init__(self, item_type, mutable=True):
         if isinstance(item_type, type):
@@ -207,7 +214,7 @@
     'real' : PGFloat,
     'inet': PGInet,
     'macaddr': PGMacAddr,
-    'double precision' : PGFloat,
+    'double precision' : PGDoublePrecision,
     'timestamp' : PGDateTime,
     'timestamp with time zone' : PGDateTime,
     'timestamp without time zone' : PGDateTime,
@@ -495,7 +502,7 @@
                     numericprec, numericscale = charlen.split(',')
                 charlen = False
             if attype == 'double precision':
-                numericprec, numericscale = (53, False)
+                numericprec, numericscale = (True, False)
                 charlen = False
             if attype == 'integer':
                 numericprec, numericscale = (32, 0)

is there a test case we can get which illustrates that this is not enough (i.e. the change to the typemap).

comment:8 Changed 4 years ago by guest

Just got back to this issue, i'll be revisiting in a week or so when I go through another reflection process.

comment:9 Changed 4 years ago by guest

Okay, your proposed patch does not work, and fails in the following way:

Traceback (most recent call last):

File "/Users/cperkins1/nrel/pdil/bin/create_pdil_db", line 8, in <module>

load_entry_point('pdil.model==0.2dev-re6b8ed6a3678', 'console_scripts', 'create_pdil_db')()

File "/Users/cperkins1/nrel/pdil/src/pdil.model/pdil/model/scripts/create_db.py", line 80, in main

create_databases(options)

File "/Users/cperkins1/nrel/pdil/src/pdil.model/pdil/model/scripts/create_db.py", line 65, in create_databases

metadata.create_all()

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/schema.py", line 1796, in create_all

bind.create(self, checkfirst=checkfirst, tables=tables)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/engine/base.py", line 1129, in create

self._run_visitor(self.dialect.schemagenerator, entity, connection=connection, kwargs)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/engine/base.py", line 1158, in _run_visitor

visitorcallable(self.dialect, conn, kwargs).traverse(element)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/sql/visitors.py", line 89, in traverse

return traverse(obj, self.traverse_options, self._visitor_dict)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/sql/visitors.py", line 200, in traverse

return traverse_using(iterate(obj, opts), obj, visitors)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/sql/visitors.py", line 194, in traverse_using

meth(target)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/sql/compiler.py", line 831, in visit_metadata

self.traverse_single(table)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/sql/visitors.py", line 79, in traverse_single

return meth(obj)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/sql/compiler.py", line 856, in visit_table

self.append("\t" + self.get_column_specification(column, first_pk=column.primary_key and not first_pk))

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/databases/postgres.py", line 816, in get_column_specification

colspec += " " + column.type.dialect_impl(self.dialect).get_col_spec()

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/types.py", line 129, in dialect_impl

return self._impl_dict.setdefault(dialect, dialect.type_descriptor(self))

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/databases/postgres.py", line 380, in type_descriptor

return sqltypes.adapt_type(typeobj, colspecs)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/types.py", line 358, in adapt_type

return typeobj.adapt(impltype)

File "/Users/cperkins1/nrel/pdil/src/SQLAlchemy-0.5.6/lib/sqlalchemy/types.py", line 601, in adapt

return impltype(precision=self.precision, scale=self.scale, asdecimal=self.asdecimal)

AttributeError?: 'PGDoublePrecision' object has no attribute 'precision'

I will see what I can do to make it work.

cheers.
-chris

comment:10 Changed 4 years ago by guest

Okay, got it working. You just have to modify the DP class as follows:

class PGDoublePrecision(sqltypes.Numeric):

def init(self, asdecimal=True, precision=None, scale=None):

self.asdecimal=asdecimal
self.precision=precision
self.scale=scale


def get_col_spec(self):

return "DOUBLE PRECISION"

I'd love to see this in the next release of SA.

cheers.
-chris

comment:11 Changed 4 years ago by zzzeek

  • Milestone changed from 0.5.xx to 0.6.0
  • Progress State changed from needs questions answered to in progress

the next version will be 0.6. Can you adapt your patch to 0.6 ?

comment:12 Changed 4 years ago by guest

Okay,

I just tested this with 0.6 (trunk) and it works without modification. What I'm saying is that your implementation of postgres is more precise in 0.6.

However, I am not sure of the timeframe of 0.6, and I would like to see this change pushed to 0.5.7 if such a thing will exist.

cheers.
-chris

comment:13 Changed 4 years ago by zzzeek

  • Resolution set to fixed
  • Status changed from new to closed

not decided on 0.5.7, but the patch is in r6d673caad3aa. the type extends Float as it does in 0.6. Also added a test for this type in 0.6 in rc21c45717694.

Note: See TracTickets for help on using tickets.