summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/selectable.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-09-20 13:43:21 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2021-09-20 15:50:07 -0400
commit5b05440b6778b8505988265dd49e968f30c900e0 (patch)
tree7a2e2d94f5f49a3e499d5bcdee7fdb5a2696799c /lib/sqlalchemy/sql/selectable.py
parent8a10b1e4506c27773628fff1f3d99ca850211862 (diff)
downloadsqlalchemy-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.py34
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]),
)
)