Dear Michael,
Thanks a lot for your reply.
In trying to narrow the problem as much as possible, I missed something
important in my example. I'm actually doing an UPDATE, not a SELECT. When I
wrote 'I tried casting my decimals using sqlalcheme.cast(...,
sqlalchemy.Numeric(precision=16, scale=8)) but that didn't work because the
filter expression failed' I meant that it didn't work in an update
scenario. In a select scenario it works ok. This is what I should have
wrote in my bug_test.py:
amount = decimal.Decimal('0.00000001')
query = session.query(Balance)
query = query.filter(
Balance.available_balance + sqlalchemy.cast(amount,
sqlalchemy.Numeric(precision=16, scale=8)) <= Balance.full_balance
)
values = {}
values[Balance.available_balance] = Balance.available_balance + amount
row_count = query.update(values)
print row_count, "rows updated"
and the error I get is 'sqlalchemy.exc.InvalidRequestError: Could not
evaluate current criteria in Python. Specify 'fetch' or False for the
synchronize_session parameter.'. This is not even getting to MySQL.
Thanks a lot
On Monday, April 13, 2015 at 2:46:14 PM UTC-3, Michael Bayer wrote:
>
>
>
> On 4/13/15 11:50 AM, Gabriel Becedillas wrote:
>
> Dear all,
> I have a table that has 2 numeric columns, and I'm writing a query that
> performs some arithmetic on the filter clause between those columns and a
> Decimal. The problem that I'm facing is that I don't get any results at
> all. After a while I realized that the SQL statement getting generated is
> dumping Decimals as strings, and when strings are involved in a numeric
> expression they get converted to floats. So, my query is not returning
> anything at all due to float representation limitations.
>
> I tried casting my decimals using sqlalcheme.cast(...,
> sqlalchemy.Numeric(precision=16, scale=8)) but that didn't work because the
> filter expression failed. Can anyone help me in getting a cast over a query
> parameter to work in a filter expression ?
>
>
> this is driver stuff. If you change the query to see what you're getting:
>
> query = session.query(Balance.available_balance + amount,
> Balance.full_balance)
>
> you can see there's some floating point noise in there, not to mention
> we're even getting the value back as a floating point:
>
> Col (u'anon_1', u'balance_full_balance')
> 2015-04-13 13:10:39,798 DEBUG sqlalchemy.engine.base.Engine Row
> (3.0000000000000004e-08, Decimal('3E-8'))
>
> I'm getting the same result with MySQL-Python, PyMySQL, and
> Mysql-connector. The issue is definitely in the drivers however, the code
> below produces no result for all three drivers:
>
> conn = engine.raw_connection()
> cursor = conn.cursor()
> cursor.execute(
> "SELECT balance.available_balance + %s AS anon_1, "
> "balance.full_balance AS balance_full_balance FROM balance "
> "WHERE balance.available_balance + %s <= balance.full_balance",
> (amount, amount))
>
> print cursor.fetchall()
>
> If I embed the number 0000000.1 as is into the query without using a
> parameter, then it works, as it does on the command line.
>
> Looking into PyMySQL since that's the driver I'm most familiar with, if we
> send a Decimal it is doing this:
>
> SELECT balance.available_balance + 1E-8 AS anon_1, balance.full_balance AS
> balance_full_balance FROM balance WHERE balance.available_balance + 1E-8 <=
> balance.full_balance
>
> What is interesting is that these values as passed in an INSERT *are*
> working correctly. Which means really, this is a MySQL bug; I can prove
> it at the command line.
>
> First, we illustrate that scientific notation *is* accepted directly by
> MySQL:
>
> mysql> insert into balance (full_balance, available_balance) values (3E-8,
> 2E-8);
> Query OK, 1 row affected (0.00 sec)
>
>
> values go in just fine (note I increased the scale in the table here,
> hence the two trailing 0's):
>
> mysql> select * from balance;
> +----+--------------+-------------------+
> | id | full_balance | available_balance |
> +----+--------------+-------------------+
> | 2 | 0.0000000300 | 0.0000000200 |
> +----+--------------+-------------------+
> 1 row in set (0.00 sec)
>
> but in the WHERE clause, *it fails*:
>
> mysql> select * from balance where available_balance + 1E-8 <=
> full_balance;
> Empty set (0.00 sec)
>
> writing out the whole value, *it succeeds*:
>
> mysql> select * from balance where available_balance + 0.00000001 <=
> full_balance;
> +----+--------------+-------------------+
> | id | full_balance | available_balance |
> +----+--------------+-------------------+
> | 2 | 0.0000000300 | 0.0000000200 |
> +----+--------------+-------------------+
> 1 row in set (0.00 sec)
>
> we can see that *MySQL itself is doing floating point*, so that's really
> the bug here:
>
> mysql> select available_balance + 1E-8 from balance;
> +----------------------------+
> | available_balance + 1E-8 |
> +----------------------------+
> | 0.000000030000000000000004 |
> +----------------------------+
> 1 row in set (0.00 sec)
>
> We can in fact make it work with a CAST. However! crazytown time. Even
> though NUMERIC and DECIMAL are equivalent in MySQL, cast will *not* accept
> NUMERIC (note SQLAlchemy only warns on these and only as of 1.0 I think):
>
> mysql> select available_balance + CAST(1E-8 AS NUMERIC) from balance;
> ERROR 1064 (42000): You have an error in your SQL syntax; check the manual
> that corresponds to your MySQL server version for the right syntax to use
> near 'NUMERIC) from balance' at line 1
>
> But it *will* accept DECIMAL:
>
> mysql> select available_balance + CAST(1E-8 AS DECIMAL) from balance;
> +-------------------------------------------+
> | available_balance + CAST(1E-8 AS DECIMAL) |
> +-------------------------------------------+
> | 0.0000000200 |
> +-------------------------------------------+
> 1 row in set (0.00 sec)
>
> So there's our answer:
>
> from sqlalchemy import cast, DECIMAL
>
> amount = decimal.Decimal('0.00000001')
> query = session.query(Balance.available_balance + cast(amount,
> DECIMAL()), Balance.full_balance)
> query = query.filter(
> Balance.available_balance + cast(amount, DECIMAL()) <=
> Balance.full_balance
> )
>
>
> SELECT balance.available_balance + CAST(%s AS DECIMAL) AS anon_1,
> balance.full_balance AS balance_full_balance
> FROM balance
> WHERE balance.available_balance + CAST(%s AS DECIMAL) <=
> balance.full_balance
> 2015-04-13 13:43:16,630 INFO sqlalchemy.engine.base.Engine
> (Decimal('1E-8'), Decimal('1E-8'))
> 2015-04-13 13:43:16,631 DEBUG sqlalchemy.engine.base.Engine Col
> (u'anon_1', u'balance_full_balance')
> 2015-04-13 13:43:16,631 DEBUG sqlalchemy.engine.base.Engine Row
> (Decimal('2E-8'), Decimal('3E-8'))
> 1 should be > 0
>
> The drivers would really be nice if they expanded out these values from
> scientific notation. However, MySQL's bizarrely inconsistent behavior
> here is probably why the need for this hasn't been observed. So stick
> with cast(value, DECIMAL()) for now when using the value in the WHERE
> clause.
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> I'm attaching a sample to reproduce the issue.
> Thanks a lot.
> --
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected] <javascript:>.
> To post to this group, send email to [email protected]
> <javascript:>.
> Visit this group at http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.
>
>
>
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.