diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-09-20 13:43:21 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-09-20 15:50:07 -0400 |
commit | 5b05440b6778b8505988265dd49e968f30c900e0 (patch) | |
tree | 7a2e2d94f5f49a3e499d5bcdee7fdb5a2696799c /lib/sqlalchemy/sql/selectable.py | |
parent | 8a10b1e4506c27773628fff1f3d99ca850211862 (diff) | |
download | sqlalchemy-5b05440b6778b8505988265dd49e968f30c900e0.tar.gz |
include setup_joins targets when scanning for FROM objects to clone
Fixed a two issues where combinations of ``select()`` and ``join()`` when
adapted to form a copy of the element would not completely copy the state
of all column objects associated with subqueries. A key problem this caused
is that usage of the :meth:`_sql.ClauseElement.params` method (which should
probably be moved into a legacy category as it is inefficient and error
prone) would leave copies of the old :class:`_sql.BindParameter` objects
around, leading to issues in correctly setting the parameters at execution
time.
Fixes: #7055
Change-Id: Ib822a978a99561b4402da3fb727b370f5c58210b
Diffstat (limited to 'lib/sqlalchemy/sql/selectable.py')
-rw-r--r-- | lib/sqlalchemy/sql/selectable.py | 34 |
1 files changed, 34 insertions, 0 deletions
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index aa218052e..970c7a0c5 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -1138,6 +1138,39 @@ class Join(roles.DMLTableRole, FromClause): itertools.chain(*[col.foreign_keys for col in columns]) ) + def _copy_internals(self, clone=_clone, **kw): + # see Select._copy_internals() for similar concept + + # here we pre-clone "left" and "right" so that we can + # determine the new FROM clauses + all_the_froms = set( + itertools.chain( + _from_objects(self.left), + _from_objects(self.right), + ) + ) + + # run the clone on those. these will be placed in the + # cache used by the clone function + new_froms = {f: clone(f, **kw) for f in all_the_froms} + + # set up a special replace function that will replace for + # ColumnClause with parent table referring to those + # replaced FromClause objects + def replace(obj, **kw): + if isinstance(obj, ColumnClause) and obj.table in new_froms: + newelem = new_froms[obj.table].corresponding_column(obj) + return newelem + + kw["replace"] = replace + + # run normal _copy_internals. the clones for + # left and right will come from the clone function's + # cache + super(Join, self)._copy_internals(clone=clone, **kw) + + self._reset_memoizations() + def _refresh_for_new_column(self, column): super(Join, self)._refresh_for_new_column(column) self.left._refresh_for_new_column(column) @@ -5519,6 +5552,7 @@ class Select( itertools.chain( _from_objects(*self._raw_columns), _from_objects(*self._where_criteria), + _from_objects(*[elem[0] for elem in self._setup_joins]), ) ) |