asyncmy icon indicating copy to clipboard operation
asyncmy copied to clipboard

OverflowError: Python int too large to convert to C long

Open 1067511899 opened this issue 3 years ago • 20 comments

Python 3.9.7 (default, Sep 16 2021, 16:59:28) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32 sqlalchemy 1.4.29

one statement is : where(paymentTime <= result) ,the result is 1642664534000. when I change the statement to where(paymentTime <= result/1000),there is no exception.

thanks.

Traceback (most recent call last): File "G:\ProgramData\Anaconda3\lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 375, in run_asgi result = await app(self.scope, self.receive, self.send) File "G:\ProgramData\Anaconda3\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 75, in call return await self.app(scope, receive, send) File "G:\ProgramData\Anaconda3\lib\site-packages\fastapi\applications.py", line 211, in call await super().call(scope, receive, send) File "G:\ProgramData\Anaconda3\lib\site-packages\starlette\applications.py", line 112, in call await self.middleware_stack(scope, receive, send) File "G:\ProgramData\Anaconda3\lib\site-packages\starlette\middleware\errors.py", line 181, in call raise exc File "G:\ProgramData\Anaconda3\lib\site-packages\starlette\middleware\errors.py", line 159, in call await self.app(scope, receive, _send) File "G:\ProgramData\Anaconda3\lib\site-packages\starlette\exceptions.py", line 82, in call raise exc File "G:\ProgramData\Anaconda3\lib\site-packages\starlette\exceptions.py", line 71, in call await self.app(scope, receive, sender) File "G:\ProgramData\Anaconda3\lib\site-packages\starlette\routing.py", line 656, in call await route.handle(scope, receive, send) File "G:\ProgramData\Anaconda3\lib\site-packages\starlette\routing.py", line 259, in handle await self.app(scope, receive, send) File "G:\ProgramData\Anaconda3\lib\site-packages\starlette\routing.py", line 61, in app response = await func(request) File "G:\ProgramData\Anaconda3\lib\site-packages\fastapi\routing.py", line 226, in app raw_response = await run_endpoint_function( File "G:\ProgramData\Anaconda3\lib\site-packages\fastapi\routing.py", line 159, in run_endpoint_function return await dependant.call(**values) File "H:\gitlabproject\pyfreepro\ormtest.\sql_app\main.py", line 44, in getmax1 db_user = await crud.getmax1(db, id=payTradeId) File "H:\gitlabproject\pyfreepro\ormtest.\sql_app\crud.py", line 88, in getmax1 newresult = await db.execute(newstmt) File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\ext\asyncio\session.py", line 211, in execute return await greenlet_spawn( File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\util_concurrency_py3k.py", line 134, in greenlet_spawn result = context.throw(*sys.exc_info()) File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\orm\session.py", line 1692, in execute result = conn._execute_20(statement, params or {}, execution_options) File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 1614, in _execute_20 return meth(self, args_10style, kwargs_10style, execution_options) File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\sql\elements.py", line 325, in _execute_on_connection return connection._execute_clauseelement( File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 1481, in _execute_clauseelement ret = self._execute_context( File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 1845, in _execute_context self.handle_dbapi_exception( File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 2030, in handle_dbapi_exception util.raise(exc_info[1], with_traceback=exc_info[2]) File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\util\compat.py", line 207, in raise raise exception File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 1802, in execute_context self.dialect.do_execute( File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\engine\default.py", line 732, in do_execute cursor.execute(statement, parameters) File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\dialects\mysql\asyncmy.py", line 92, in execute return self.await(self._execute_async(operation, parameters)) File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\util_concurrency_py3k.py", line 76, in await_only return current.driver.switch(awaitable) File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\util_concurrency_py3k.py", line 129, in greenlet_spawn value = await result File "G:\ProgramData\Anaconda3\lib\site-packages\sqlalchemy\dialects\mysql\asyncmy.py", line 104, in _execute_async result = await self._cursor.execute(operation, parameters) File "asyncmy\cursors.pyx", line 178, in execute File "asyncmy\cursors.pyx", line 156, in asyncmy.cursors.Cursor.mogrify File "asyncmy\cursors.pyx", line 129, in genexpr File "asyncmy\cursors.pyx", line 129, in genexpr File "asyncmy\connection.pyx", line 426, in asyncmy.connection.Connection.literal File "asyncmy\connection.pyx", line 419, in asyncmy.connection.Connection.escape File "asyncmy\converters.pyx", line 10, in asyncmy.converters.escape_item File "asyncmy\converters.pyx", line 25, in asyncmy.converters.escape_item File "asyncmy\converters.pyx", line 48, in asyncmy.converters.escape_int OverflowError: Python int too large to convert to C long

1067511899 avatar Jan 20 '22 08:01 1067511899

As far as I can see, this is caused by the number which is bigger than long long's range in C, remove the long long in cpdef should work cause that tells cython to use python's PyLongObject instead of long long

synodriver avatar Feb 08 '22 12:02 synodriver

By changing this function

cpdef escape_int(long long value, mapping=None):
    return str(value)

to

cpdef escape_int(object value, mapping=None):
    return str(value)

synodriver avatar Feb 08 '22 12:02 synodriver

It might works, however, this may lead to a performance degradation due to the price that interact with PyObject.

synodriver avatar Feb 08 '22 12:02 synodriver

What about use 64 bit Python?

long2ice avatar Feb 10 '22 03:02 long2ice

I have the same problem.
It occurs on windows(64bit) when parsing bigint. It's ok in Linux

yinziyan1206 avatar Mar 03 '22 03:03 yinziyan1206

I have the same problem. It occurs on windows(64bit) when parsing bigint. It's ok in Linux

i use python 3.9 amd64

yinziyan1206 avatar Mar 03 '22 03:03 yinziyan1206

I have the same problem. Unfortunately, this puts an end to using asyncmy for discord bot.

Windows 10 64 bit Python 3.10 amd64

Here is the traceback/logs:

INFO sqlalchemy.engine.Engine BEGIN (implicit)
INFO sqlalchemy.engine.Engine INSERT INTO queue_data (message_id, task_id, `index`) VALUES (%s, %s, %s)
INFO sqlalchemy.engine.Engine (1099700317114863759, 7, 0)
    result = await self._cursor.execute(operation, parameters)
OverflowError: Python int too large to convert to C unsigned long
Traceback (most recent call last):
  File "venv\lib\site-packages\sqlalchemy\dialects\mysql\asyncmy.py", line 104, in _execute_async
INFO:sqlalchemy.engine.Engine:COMMIT

SQLAlchemy model:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, BigInteger

Base = declarative_base()


class BaseModel(Base):
    __abstract__ = True


class QueueData(BaseModel):
    __tablename__ = "queue_data"

    message_id = Column(BigInteger, nullable=False, primary_key=True, unique=True)
    task_id = Column(Integer, nullable=False)
    index = Column(Integer, nullable=False)

quswadress avatar Apr 24 '23 02:04 quswadress

What's the asyncmy version?

long2ice avatar Apr 24 '23 02:04 long2ice

@long2ice, asyncmy 0.2.7 and legacy SQLAlchemy 1.4.47

quswadress avatar Apr 24 '23 02:04 quswadress

Try 0.2.8rc1, I test in local but can't repeated

long2ice avatar Apr 25 '23 10:04 long2ice

0.2.8rc1 also raises an exception.

With new SQLAlchemy 2.0.12 the error is the same.

Here is a simple code:

import asyncio

from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, BigInteger

Base = declarative_base()


class BaseModel(Base):
    __abstract__ = True


class QueueData(BaseModel):
    __tablename__ = "queue_data"

    message_id = Column(BigInteger, nullable=False, primary_key=True, unique=True)
    task_id = Column(Integer, nullable=False)
    index = Column(Integer, nullable=False)


async def main():
    engine = create_async_engine("mysql+asyncmy://user:pass@localhost:3307/bot", echo=True)
    await engine.connect()
    made_session = async_sessionmaker(engine)

    async with engine.begin() as connection:
        await connection.run_sync(lambda engine: Base.metadata.create_all(engine, checkfirst=True))
    async with made_session() as session:
        session.add(QueueData(message_id=int(5e+18), task_id=7, index=0))
        await session.commit()


if __name__ == '__main__':
    asyncio.run(main())

And here is its output:

2023-05-08 04:33:04,766 INFO sqlalchemy.engine.Engine SELECT DATABASE()
2023-05-08 04:33:04,766 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-05-08 04:33:04,768 INFO sqlalchemy.engine.Engine SELECT @@sql_mode
2023-05-08 04:33:04,768 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-05-08 04:33:04,769 INFO sqlalchemy.engine.Engine SELECT @@lower_case_table_names
2023-05-08 04:33:04,769 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-05-08 04:33:04,775 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-05-08 04:33:04,775 INFO sqlalchemy.engine.Engine DESCRIBE `bot`.`queue_data`
2023-05-08 04:33:04,775 INFO sqlalchemy.engine.Engine [raw sql] ()
  await engine.connect()
OverflowError: Python int too large to convert to C unsigned long
Exception ignored in: 'asyncmy.protocol.MysqlPacket.read_uint64'
Traceback (most recent call last):
  File "...\site-packages\sqlalchemy\dialects\mysql\asyncmy.py", line 107, in _execute_async
    result = await self._cursor.execute(operation, parameters)
OverflowError: Python int too large to convert to C unsigned long
2023-05-08 04:33:04,777 INFO sqlalchemy.engine.Engine COMMIT
2023-05-08 04:33:04,780 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-05-08 04:33:04,781 INFO sqlalchemy.engine.Engine INSERT INTO queue_data (message_id, task_id, `index`) VALUES (%s, %s, %s)
2023-05-08 04:33:04,781 INFO sqlalchemy.engine.Engine [generated in 0.00017s] (5000000000000000000, 7, 0)
2023-05-08 04:33:04,783 INFO sqlalchemy.engine.Engine COMMIT

quswadress avatar May 08 '23 01:05 quswadress

What's the result of python -c 'from asyncmy.version import __VERSION__;print(__VERSION__)'?

long2ice avatar May 08 '23 01:05 long2ice

> python -c 'from asyncmy.version import __VERSION__;print(__VERSION__)'
0.2.8-rc1

I already said the version in the previous comment.

0.2.8rc1 also raises an exception. With new SQLAlchemy 2.0.12 the error is the same.

quswadress avatar May 08 '23 12:05 quswadress

Yes, I just confirm it. But I really can't repeated it.

2023-05-08 20:29:47,580 INFO sqlalchemy.engine.Engine SELECT DATABASE()
2023-05-08 20:29:47,580 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-05-08 20:29:47,581 INFO sqlalchemy.engine.Engine SELECT @@sql_mode
2023-05-08 20:29:47,581 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-05-08 20:29:47,581 INFO sqlalchemy.engine.Engine SELECT @@lower_case_table_names
2023-05-08 20:29:47,581 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-05-08 20:29:47,584 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-05-08 20:29:47,585 INFO sqlalchemy.engine.Engine DESCRIBE `test`.`queue_data`
2023-05-08 20:29:47,585 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-05-08 20:29:47,586 INFO sqlalchemy.engine.Engine 
CREATE TABLE queue_data (
	message_id BIGINT NOT NULL AUTO_INCREMENT, 
	task_id INTEGER NOT NULL, 
	`index` INTEGER NOT NULL, 
	PRIMARY KEY (message_id), 
	UNIQUE (message_id)
)


2023-05-08 20:29:47,586 INFO sqlalchemy.engine.Engine [no key 0.00009s] ()
The garbage collector is trying to clean up non-checked-in connection <AdaptedConnection <asyncmy.connection.Connection object at 0x10f841d60>>, which will be dropped, as it cannot be safely terminated.  Please ensure that SQLAlchemy pooled connections are returned to the pool explicitly, either by calling ``close()`` or by using appropriate context managers to manage their lifecycle.
/Users/long2ice/PycharmProjects/asyncmy/examples/main.py:24: SAWarning: The garbage collector is trying to clean up non-checked-in connection <AdaptedConnection <asyncmy.connection.Connection object at 0x10f841d60>>, which will be dropped, as it cannot be safely terminated.  Please ensure that SQLAlchemy pooled connections are returned to the pool explicitly, either by calling ``close()`` or by using appropriate context managers to manage their lifecycle.
  await engine.connect()
2023-05-08 20:29:47,618 INFO sqlalchemy.engine.Engine COMMIT
2023-05-08 20:29:47,621 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-05-08 20:29:47,622 INFO sqlalchemy.engine.Engine INSERT INTO queue_data (message_id, task_id, `index`) VALUES (%s, %s, %s)
2023-05-08 20:29:47,622 INFO sqlalchemy.engine.Engine [generated in 0.00016s] (5000000000000000000, 7, 0)
2023-05-08 20:29:47,623 INFO sqlalchemy.engine.Engine COMMIT

long2ice avatar May 08 '23 12:05 long2ice

Hmm, maybe it depends on the version of mysql and how you run it? I ran it in WSL2(Ubuntu 22.04) through the latest(8.0.33) docker image mysql.

quswadress avatar May 08 '23 13:05 quswadress

I run it in macos, mysql 8.0.32

long2ice avatar May 08 '23 13:05 long2ice

Tried mysql 8.0.32 and the exception still raises. 😞

quswadress avatar May 08 '23 13:05 quswadress

That's strange

long2ice avatar May 08 '23 13:05 long2ice

Can you replicate this exception by running on Windows/Linux? Something just tells me that the problem lies in primitive data types. That's why we have an overflow error.

quswadress avatar May 13 '23 06:05 quswadress

Hi @long2ice, I experience the same problem with unsigned big ints (values over 2^63 - 1)

MRE

import asyncio
import asyncmy


async def create_table(cursor):
    query = """
    CREATE TABLE IF NOT EXISTS foo (
        bar BIGINT UNSIGNED PRIMARY KEY
    )
    """
    await cursor.execute(query)


async def insert_overflow(cursor):
    query = """
    INSERT INTO foo (bar) VALUES (%s)
    """
    params = (2**64 - 1,)
    await cursor.execute(query, params)


async def main():
    conn = await asyncmy.connect(
        host="db", port=3306, user="mysql1", password="mysql1", database="mysql1"
    )
    async with conn.cursor() as cursor:
        await create_table(cursor)
        await insert_overflow(cursor)


if __name__ == "__main__":
    asyncio.run(main())

It yields the same error as in the initial post in this thread

  File "asyncmy/cursors.pyx", line 177, in execute
  File "asyncmy/cursors.pyx", line 155, in asyncmy.cursors.Cursor.mogrify
  File "asyncmy/cursors.pyx", line 128, in genexpr
  File "asyncmy/cursors.pyx", line 128, in genexpr
  File "asyncmy/connection.pyx", line 421, in asyncmy.connection.Connection.literal
  File "asyncmy/connection.pyx", line 414, in asyncmy.connection.Connection.escape
  File "asyncmy/converters.pyx", line 11, in asyncmy.converters.escape_item
  File "asyncmy/converters.pyx", line 26, in asyncmy.converters.escape_item
  File "asyncmy/converters.pyx", line 49, in asyncmy.converters.escape_int
OverflowError: Python int too large to convert to C long

Solution

I think it can be solved in at least 2 ways

  1. Removing C typing in the escape_int function
cpdef str escape_int(long long value, mapping: dict = None):
    return str(value)

would become

cpdef str escape_int(value, mapping: dict = None):
    return str(value)

This may lead to a performance degradation as mentioned by @synodriver.

  1. Introducing new C typed escape function for unsigned big ints
cpdef str escape_bigint_unsigned(unsigned long long value, mapping: dict = None):
    return str(value)

which would be applied for the problematic values in escape_item

cpdef escape_item(val, str charset, mapping: dict = None):
    if mapping is None:
        mapping = encoders
    encoder = mapping.get(type(val))

    # Fallback to default when no encoder found
    if not encoder:
        try:
            encoder = mapping[str]
        except KeyError:
            raise TypeError("no default type converter defined")

    if encoder in (escape_dict, escape_sequence):
        val = encoder(val, charset, mapping)
    else:
        val = encoder(val, mapping)
    return val

would become

MAX_SIGNED_INT = 2**63 - 1

cpdef escape_item(val, str charset, mapping: dict = None):
    if mapping is None:
        mapping = encoders
    type_ = type(val)
    if type_ is int and val > MAX_SIGNED_INT:
        encoder = escape_bigint_unsigned
    else:
        encoder = mapping.get(type_)

    # Fallback to default when no encoder found
    if not encoder:
        try:
            encoder = mapping[str]
        except KeyError:
            raise TypeError("no default type converter defined")

    if encoder in (escape_dict, escape_sequence):
        val = encoder(val, charset, mapping)
    else:
        val = encoder(val, mapping)
    return val

Here the performance could be impacted by additional comparisons.

Evaluation

I ran your benchmarks against current asyncmy dev branch and both modifications with COUNT=100000 including new column for random integers up to 2^63 - 1.

current no typing new escape
insert 13.98002 14.37959 13.95373
select 0.83498 0.86191 0.84803
update 13.13448 12.84223 12.93422
delete 13.28940 13.04953 12.59234

I don't think differences on this benchmark are significant.


Hope this can help

Greenpp avatar Aug 17 '23 08:08 Greenpp