negation operator does not preserve original expression type

Issue #3735 resolved
Roman Dubina created an issue

When using

DECIMAL(asdecimal=False)

with case statement SQLAlchemy not always casts Decimal to float.

Here is a working example how to reproduce the problem with output. I don't know exactly how is this happening:

from __future__ import print_function
from sqlalchemy import Column, Integer
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.scoping import scoped_session
from sqlalchemy.types import DECIMAL
from sqlalchemy import case

conn_string = '...'

engine = create_engine(conn_string,
                       convert_unicode=True,
                       echo=False,
                       connect_args={'application_name': 'myApp'})

session = scoped_session(sessionmaker(bind=engine))
base = declarative_base()
base.query = session.query_property()

class TestDecimal(base):
    __tablename__ = 'test_decimal'

    id = Column(Integer, primary_key=True, autoincrement=True)
    money = Column(DECIMAL(asdecimal=False))
    state = Column(Integer)

    def __init__(self, money, state):
        self.money = money
        self.state = state

base.metadata.bind = session.bind
base.metadata.create_all()


if __name__ == '__main__':
    session.add(TestDecimal(1.0, 1))
    session.add(TestDecimal(2.0, 2))
    session.commit()

    rec = session.query(case([(TestDecimal.state == 1, -TestDecimal.money),
                              (TestDecimal.state == 2, TestDecimal.money)], else_=0)).filter().all()

    print(rec)

    rec = session.query(case([(TestDecimal.state == 1, TestDecimal.money),
                              (TestDecimal.state == 2, -TestDecimal.money)], else_=0)).filter().all()

    print(rec)

which prints:

[(-1.0,), (2.0,)]
[(Decimal('1.0'),), (Decimal('-2.0'),)]

you can see that two case statements almost identical except of minus sign. That is weird.

Comments (9)

  1. Mike Bayer repo owner

    it's really critical which DBAPI this is. the issue is likely that this datatype is already delivered as a float normally from the driver, but in the case of a "case" statement the database is not sending typing information back. a cast() here would likely resolve (on your end).

  2. Mike Bayer repo owner

    Preserve type for math negation

    Fixed issue in SQL math negation operator where the type of the expression would no longer be the numeric type of the original. This would cause issues where the type determined result set behaviors.

    Change-Id: If0e339614a3686e251235fc94b6f59310c4630a5 Fixes: #3735

    → <<cset 5c60aaefd32a>>

  3. Mike Bayer repo owner

    Preserve type for math negation

    Fixed issue in SQL math negation operator where the type of the expression would no longer be the numeric type of the original. This would cause issues where the type determined result set behaviors.

    Change-Id: If0e339614a3686e251235fc94b6f59310c4630a5 Fixes: #3735 (cherry picked from commit 5c60aaefd32a7bdee611fb340911409e0b8223ed)

    → <<cset 9880da342f28>>

  4. Log in to comment