mapper _postfetch error regarding data conversion

Issue #110 resolved
Former user created an issue

Try this:

from sqlalchemy import *
engine = create_engine('sqlite://filename=:memory:', encoding='latin2')

test = Table('test', engine, 
    Column('id',  Integer, primary_key=True),
    Column('txt', Unicode(50)))
test.create()
class Test(object):
    pass
assign_mapper(Test, test)

txt = u"\u0160\u0110\u0106\u010c\u017d"
t1 = Test(id=1, txt = txt)
assert t1.txt == txt

objectstore.commit() # this will raise exception at mapper.py(654)

You will get:

File "...\sqlalchemy\mapping\mapper.py", line 654, in _postfetch
  if self._getattrbycolumn(obj, c) != params[c.name](c.name):
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa9 in position 0: ordinal not in range(128)

_postfetch fails because _getattrbycolumn returns unicode and params[c.name](c.name) contains converted value.

I guess that not only unicode suffers from this line of code. This line is comparing original value (unicode) with converted values which is prepared for database.

Comments (2)

  1. Former user Account Deleted

    I did the following as a temporary workaround:

    Index: lib/sqlalchemy/mapping/mapper.py
    ===================================================================
    --- lib/sqlalchemy/mapping/mapper.py    (revision 1126)
    +++ lib/sqlalchemy/mapping/mapper.py    (working copy)
    @@ -651,8 +651,9 @@
                 for c in table.c:
                     if c.primary_key or not params.has_key(c.name):
                         continue
    -                if self._getattrbycolumn(obj, c) != params[c.name](c.name):
    -                    self._setattrbycolumn(obj, c, params[c.name](c.name))
    +                val = c.type.convert_result_value(params[c.name](c.name), table.engine)
    +                if self._getattrbycolumn(obj, c) != val:
    +                    self._setattrbycolumn(obj, c, val)
    
         def delete_obj(self, objects, uow):
             """called by a UnitOfWork object to delete objects, which involves a
    

    It allows for this code to pass:

    assert t1.txt == txt
    objectstore.commit()
    assert t1.txt == txt
    

    But I'm not entirely satisified because this (less-correct code) will fail:

    t1 = Test(id=1, txt = txt.encode('latin2'))
    assert t1.txt == txt.encode('latin2')
    objectstore.commit() # this will again raise exception at mapper.py
    

    Even if it passed, value of t1.txt would be different after commit().

  2. Log in to comment