Skip to content

Changing column order causes baked tuple_ query to raise TypeError #5393

@acowlikeobject

Description

@acowlikeobject

Describe the bug
If I create a "regular" tuple_ query, it works as expected, even if I switch columns around in tuple_.

However, switching column order causes the baked variant of the same tuple_ query to raise a sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite Date type only accepts Python date objects as input.. Seems to happen with Date and DateTime columns, but that may be because most of my other columns have interchangeable types.

May very well be user error. Would love to know what I'm doing wrong with the baked query.

Expected behavior
All queries return a count of 1.

To Reproduce

from sqlalchemy import Column, Date, Integer, create_engine, tuple_, bindparam
from sqlalchemy.ext.baked import bakery as bakery_maker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session

from datetime import date


Base = declarative_base()

class Foo(Base):
    __tablename__ = 'foo'
    id = Column(Integer, primary_key=True)
    first = Column(Integer)
    second = Column(Date)


# Setup.
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
session = Session(bind=engine)

# Insert a row.
foo = Foo(first=1, second=date(2000, 1, 1))
session.add(foo)


def make_regular_query(columns, values):
    return session.query(Foo).filter(tuple_(*columns).in_(values))


def make_baked_query(columns):
    return bakery_maker()(lambda session: session.query(Foo)
        .filter(tuple_(*columns).in_(bindparam('values', expanding=True))))


def run():
    # (first, second) order.
    cols = Foo.first, Foo.second
    values = [(foo.first, foo.second)]

    print('(first, second) regular query',
          make_regular_query(cols, values).count())

    print('(first, second) baked query', 
          make_baked_query(cols)(session).params(values=values).count())


    # (second,) order.
    cols = (Foo.second,)
    values = [(foo.second,)]

    print('(second,) regular query',
          make_regular_query(cols, values).count())

    print('(second,) baked query',
          make_baked_query(cols)(session).params(values=values).count())


    # (second, first) order.
    cols = Foo.second, Foo.first
    values = [(foo.second, foo.first)]

    print('(second, first) regular query',
          make_regular_query(cols, values).count())

    print('(second, first) baked query',
          make_baked_query(cols)(session).params(values=values).count())
    # sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite Date type only accepts Python date objects as input.

run()

Error

(first, second) regular query 1                                                                           
(first, second) baked query 1
(second,) regular query 1                                                                                
(second,) baked query 1                                                                                  
(second, first) regular query 1
Traceback (most recent call last):
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1211, in _execute_context
    context = constructor(dialect, self, conn, *args) 
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 836, in _init_compiled
    param.append(processors[key](compiled_params[key]))
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/dialects/sqlite/base.py", line 837, in process
    "SQLite Date type only accepts Python "
TypeError: SQLite Date type only accepts Python date objects as input.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test.py", line 73, in <module>
    run()
  File "test.py", line 69, in run
    make_baked_query(cols)(session).params(values=values).count())
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/ext/baked.py", line 461, in count
    return bq.for_session(self.session).params(self._params).scalar()
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/ext/baked.py", line 474, in scalar
    ret = self.one()
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/ext/baked.py", line 505, in one
    ret = self.one_or_none()
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/ext/baked.py", line 527, in one_or_none 
    ret = list(self)
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/ext/baked.py", line 445, in __iter__
    return q._execute_and_instances(context)
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/orm/query.py", line 3506, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1020, in execute
    return meth(self, multiparams, params)
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/sql/elements.py", line 298, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1139, in _execute_clauseelement
    distilled_params,
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1214, in _execute_context
    e, util.text_type(statement), parameters, None, None
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1518, in _handle_dbapi_exception
    sqlalchemy_exception, with_traceback=exc_info[2], from_=e
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 178, in raise_
    raise exception
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1211, in _execute_context
    context = constructor(dialect, self, conn, *args) 
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 836, in _init_compiled
    param.append(processors[key](compiled_params[key]))
  File "/home/user/.local/lib/python3.7/site-packages/sqlalchemy/dialects/sqlite/base.py", line 837, in process
    "SQLite Date type only accepts Python "
sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite Date type only accepts Python date objects as input.
[SQL: SELECT count(*) AS count_1 
FROM (SELECT foo.id AS foo_id, foo.first AS foo_first, foo.second AS foo_second 
FROM foo 
WHERE (foo.second, foo.first) IN ([EXPANDING_values])) AS anon_1]
[parameters: [{'values': [(datetime.date(2000, 1, 1), 1)]}]]

Additional context
If I (re)use a single bakery throughout (bakery = bakery_maker() in the setup), I get other unpredictable behavior from the baked queries (like no results if I switch column order), so I'm guessing running that each time I bake a query is the right way to go about it.

Versions.

  • OS: Ubuntu 18.04
  • Python: Python 3.7.4
  • SQLAlchemy: 1.3.17
  • Database: SQLite

Have a nice day!

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionissue where a "fix" on the SQLAlchemy side is unlikely, hence more of a usage question

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions