hiku icon indicating copy to clipboard operation
hiku copied to clipboard

FieldsQuery does not work for aiopg + sqlalchemy >= 1.4

Open kindermax opened this issue 1 year ago • 1 comments

aiopg does not compatible with sqlalchemy >= 1.4.

Error occurs when we pass into select same field twice:

  File "/Users/max/code/evo/hiku/__pypackages__/3.11/lib/aiopg/sa/result.py", line 252, in __init__
    self._init_metadata()
  File "/Users/max/code/evo/hiku/__pypackages__/3.11/lib/aiopg/sa/result.py", line 304, in _init_metadata
    self._metadata = ResultMetaData(self, cursor_description)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/max/code/evo/hiku/__pypackages__/3.11/lib/aiopg/sa/result.py", line 97, in __init__
    map_type, map_column_name = self.result_map(result_proxy._result_map)
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/max/code/evo/hiku/__pypackages__/3.11/lib/aiopg/sa/result.py", line 174, in result_map
    priority_name = getattr(elem[2][0], "key", None) or name
                            ~~~~~~~^^^
IndexError: tuple index out of range

Reproducible example:

pip install aiopg=1.4.0 sqlalchemy==1.4.48
import aiopg.sa
import sqlalchemy

from sqlalchemy.types import Integer, Unicode
from sqlalchemy.schema import MetaData, Table, Column, ForeignKey

metadata = MetaData()

foo_table = Table(
    "foo",
    metadata,
    Column("id", Integer, primary_key=True, autoincrement=True),
    Column("name", Unicode),
    Column("count", Integer),
    Column("bar_id", ForeignKey("bar.id")),
)

pg_dsn = f'postgresql://postgres:postgres@localhost:5432/postgres'
db_dsn = 'postgresql://postgres:postgres@localhost:5432/mydb'

def setup_db(db_engine):
    metadata.create_all(db_engine)
    for r in [
        {"name": "foo1", "count": 5, "bar_id": 1},
    ]:
        db_engine.execute(foo_table.insert(), r)

db_engine = sqlalchemy.create_engine(db_dsn)
setup_db(db_engine)
db_engine.dispose()

sa_engine = await aiopg.sa.create_engine(db_dsn, minsize=0)

ids = [3,2,1]
fields_ = ['name', 'count', 'bar_id', 'bar_id']. # <-- HERE bar declared twice
columns = [foo_table.c[f] for f in fields_]
(primary_key,) = foo_table.primary_key
expr = (
    sqlalchemy.select([primary_key] + columns)
    .select_from(foo_table)
    .where(primary_key == sqlalchemy.any_(ids))
)
async with sa_engine.acquire() as connection:
    res = await connection.execute(expr)

Patch that fixes problem:

from aiopg.sa.result import ResultMetaData

    def result_map(self: Any, data_map: dict) -> Tuple[dict, dict]:
        data_map = data_map or {}
        map_type = {}
        map_column_name = {}
        for elem in data_map:
            name = elem[0]
            try:
                priority_name = getattr(elem[2][0], "key", None) or name
            except IndexError:
                map_column_name[name] = name
            else:
                map_column_name[name] = priority_name
            map_type[name] = elem[3]  # type column
        return map_type, map_column_name

    ResultMetaData.result_map = result_map

kindermax avatar May 21 '23 18:05 kindermax