diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2021-04-29 21:28:32 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2021-04-29 21:28:32 +0000 |
| commit | d264cb8529cea6ec6af84bcd202a8494d0860066 (patch) | |
| tree | f12b1fa78a6a6645d42b6df423dc7d6dca8ea6d5 | |
| parent | 8716e54b5b6d9ec20d40169de3d60db3da3ad41c (diff) | |
| parent | 7002b2b0b109e70b47d4ae4f5f6a4d7df5586f21 (diff) | |
| download | sqlalchemy-d264cb8529cea6ec6af84bcd202a8494d0860066.tar.gz | |
Merge "Ensure iterable passed to Select is not a mapped class"
| -rw-r--r-- | doc/build/changelog/unreleased_14/6300.rst | 10 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/selectable.py | 14 | ||||
| -rw-r--r-- | test/sql/test_select.py | 48 |
3 files changed, 70 insertions, 2 deletions
diff --git a/doc/build/changelog/unreleased_14/6300.rst b/doc/build/changelog/unreleased_14/6300.rst new file mode 100644 index 000000000..30711a6d8 --- /dev/null +++ b/doc/build/changelog/unreleased_14/6300.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, regression, sql + :tickets: 6300 + + Fixed regression caused by :ticket:`5395` where tuning back the check for + sequences in :func:`_sql.select` now caused failures when doing 2.0-style + querying with a mapped class that also happens to have an ``__iter__()`` + method. Tuned the check some more to accommodate this as well as some other + interesting ``__iter__()`` scenarios. + diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index ff830dbf6..43ba0da4c 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -56,6 +56,7 @@ from .elements import UnaryExpression from .visitors import InternalTraversal from .. import exc from .. import util +from ..inspection import inspect if util.TYPE_CHECKING: from typing import Any @@ -4959,8 +4960,17 @@ class Select( """ if ( args - and hasattr(args[0], "__iter__") - and not isinstance(args[0], util.string_types + (ClauseElement,)) + and ( + isinstance(args[0], list) + or ( + hasattr(args[0], "__iter__") + and not isinstance( + args[0], util.string_types + (ClauseElement,) + ) + and inspect(args[0], raiseerr=False) is None + and not hasattr(args[0], "__clause_element__") + ) + ) ) or kw: return cls.create_legacy_select(*args, **kw) else: diff --git a/test/sql/test_select.py b/test/sql/test_select.py index 96c6abd07..1dfb4cd19 100644 --- a/test/sql/test_select.py +++ b/test/sql/test_select.py @@ -82,6 +82,54 @@ class FutureSelectTest(fixtures.TestBase, AssertsCompiledSQL): "WHERE mytable.myid = myothertable.otherid", ) + def test_new_calling_style_clauseelement_thing_that_has_iter(self): + class Thing(object): + def __clause_element__(self): + return table1 + + def __iter__(self): + return iter(["a", "b", "c"]) + + stmt = select(Thing()) + self.assert_compile( + stmt, + "SELECT mytable.myid, mytable.name, " + "mytable.description FROM mytable", + ) + + def test_new_calling_style_inspectable_ce_thing_that_has_iter(self): + class Thing(object): + def __iter__(self): + return iter(["a", "b", "c"]) + + class InspectedThing(object): + def __clause_element__(self): + return table1 + + from sqlalchemy.inspection import _inspects + + @_inspects(Thing) + def _ce(thing): + return InspectedThing() + + stmt = select(Thing()) + self.assert_compile( + stmt, + "SELECT mytable.myid, mytable.name, " + "mytable.description FROM mytable", + ) + + def test_new_calling_style_thing_ok_actually_use_iter(self): + class Thing(object): + def __iter__(self): + return iter([table1.c.name, table1.c.description]) + + stmt = select(Thing()) + self.assert_compile( + stmt, + "SELECT mytable.name, mytable.description FROM mytable", + ) + def test_kw_triggers_old_style(self): assert_raises_message( |
