encode / databases

Async database support for Python. 🗄
https://www.encode.io/databases/
BSD 3-Clause "New" or "Revised" License
3.8k stars 260 forks source link

Empty list erroneously converted to empty dict in `values` argument #494

Open CobaltCause opened 2 years ago

CobaltCause commented 2 years ago

This code:

import asyncio

from databases import Database

async def amain():
    db = Database(
        # a url
    )

    await db.connect()

    query = """
        SELECT
            *
        FROM things
        WHERE
            things.id IN :ids
    """

    # the empty list here causes problems
    x = await db.fetch_all(query, values={"ids": []})

    # do stuff with x

def main():
    asyncio.run(amain())

Produces:

Traceback (most recent call last):                                                                                                                      
  File "<string>", line 1, in <module>                                                                                                                  
  File "/home/charles/science/python/playground/playground/main.py", line 32, in main                                                                   
    asyncio.run(amain())                                                                                                                                
  File "/nix/store/40n9pd613v8fc3x39yjrgs1i7d4q8yl0-python3-3.10.4/lib/python3.10/asyncio/runners.py", line 44, in run                                  
    return loop.run_until_complete(main)                                                                                                                
  File "/nix/store/40n9pd613v8fc3x39yjrgs1i7d4q8yl0-python3-3.10.4/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete              
    return future.result()                                                                                                                              
  File "/home/charles/science/python/playground/playground/main.py", line 25, in amain                                                                  
    await db.fetch_all(query, values={"ids": []})                                                                                                       
  File "/nix/store/0cinbzhji6r8l5iggrv53ymfm3awhn4n-python3-3.10.4-env/lib/python3.10/site-packages/databases/core.py", line 142, in fetch_all          
    return await connection.fetch_all(query, values)                                                                                                    
  File "/nix/store/0cinbzhji6r8l5iggrv53ymfm3awhn4n-python3-3.10.4-env/lib/python3.10/site-packages/databases/core.py", line 250, in fetch_all          
    return await self._connection.fetch_all(built_query)                                                                                                
  File "/nix/store/0cinbzhji6r8l5iggrv53ymfm3awhn4n-python3-3.10.4-env/lib/python3.10/site-packages/databases/backends/aiopg.py", line 125, in fetch_all
    await cursor.execute(query_str, args)                                                                                                               
  File "/nix/store/0cinbzhji6r8l5iggrv53ymfm3awhn4n-python3-3.10.4-env/lib/python3.10/site-packages/aiopg/connection.py", line 426, in execute          
    await self._conn._poll(waiter, timeout)                                                                                                             
  File "/nix/store/0cinbzhji6r8l5iggrv53ymfm3awhn4n-python3-3.10.4-env/lib/python3.10/site-packages/aiopg/connection.py", line 881, in _poll            
    await asyncio.wait_for(self._waiter, timeout)                                                                                                       
  File "/nix/store/40n9pd613v8fc3x39yjrgs1i7d4q8yl0-python3-3.10.4/lib/python3.10/asyncio/tasks.py", line 445, in wait_for                              
    return fut.result()                                                                                                                                 
  File "/nix/store/0cinbzhji6r8l5iggrv53ymfm3awhn4n-python3-3.10.4-env/lib/python3.10/site-packages/aiopg/connection.py", line 788, in _ready           
    state = self._conn.poll()                                                                                                                           
psycopg2.errors.SyntaxError: syntax error at or near "'{}'"                                                                                             
LINE 6:             things.id IN '{}'                                                                                                       

I expected the query to run successfully and x to be an empty list.

CobaltCause commented 2 years ago

Oh, even if this coercion didn't happen, https://github.com/encode/databases/issues/286 would be hit. Further, it turns out that just changing things.id IN :ids to things.id = ANY(:ids) makes it work as-is. Both of these things should probably get documented prominently somewhere.

misrasaurabh1 commented 2 years ago

databases should support the IN clause as it is elementary SQL and is compatible with sqlalchemy as well. Users who use IN also get presented a meaningless error which doesn't help. I also got tripped on this error.