diff options
| author | Eric Masseran <eric.masseran@gmail.com> | 2021-10-08 10:02:58 -0400 |
|---|---|---|
| committer | mike bayer <mike_mp@zzzcomputing.com> | 2021-10-12 22:46:57 +0000 |
| commit | ee9b8836a160484733baa556c5d3ade4810aa999 (patch) | |
| tree | 623c4fa6e17d2366934b931a9695f12dc1a34e9f /lib/sqlalchemy/sql/selectable.py | |
| parent | de9db9940fbcf32ccd93169d2ed6aa874869b84d (diff) | |
| download | sqlalchemy-ee9b8836a160484733baa556c5d3ade4810aa999.tar.gz | |
Fix recursive CTE to support nesting
Repaired issue in new :paramref:`_sql.HasCTE.cte.nesting` parameter
introduced with :ticket:`4123` where a recursive :class:`_sql.CTE` using
:paramref:`_sql.HasCTE.cte.recursive` in typical conjunction with UNION
would not compile correctly. Additionally makes some adjustments so that
the :class:`_sql.CTE` construct creates a correct cache key.
Pull request courtesy Eric Masseran.
Fixes: #4123
> This has not been caught by the tests because the nesting recursive
queries there did not union against itself, eg there was only the i
root clause...
- Now tests are real recursive queries
- Add tests on aliased nested CTEs (recursive or not)
- Adapt the `_restates` attribute to use it as a reference
- Add some docs around to explain some variables usage
Closes: #7133
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/7133
Pull-request-sha: 2633f34f7f5336a4a85bd3f71d07bca33ce27a2c
Change-Id: I15512c94e1bc1f52afc619d82057ca647d274e92
Diffstat (limited to 'lib/sqlalchemy/sql/selectable.py')
| -rw-r--r-- | lib/sqlalchemy/sql/selectable.py | 18 |
1 files changed, 14 insertions, 4 deletions
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index 8e71dfb97..616df0d05 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -2049,8 +2049,9 @@ class CTE( AliasedReturnsRows._traverse_internals + [ ("_cte_alias", InternalTraversal.dp_clauseelement), - ("_restates", InternalTraversal.dp_clauseelement_list), + ("_restates", InternalTraversal.dp_clauseelement), ("recursive", InternalTraversal.dp_boolean), + ("nesting", InternalTraversal.dp_boolean), ] + HasPrefixes._has_prefixes_traverse_internals + HasSuffixes._has_suffixes_traverse_internals @@ -2075,13 +2076,14 @@ class CTE( recursive=False, nesting=False, _cte_alias=None, - _restates=(), + _restates=None, _prefixes=None, _suffixes=None, ): self.recursive = recursive self.nesting = nesting self._cte_alias = _cte_alias + # Keep recursivity reference with union/union_all self._restates = _restates if _prefixes: self._prefixes = _prefixes @@ -2125,7 +2127,7 @@ class CTE( name=self.name, recursive=self.recursive, nesting=self.nesting, - _restates=self._restates + (self,), + _restates=self, _prefixes=self._prefixes, _suffixes=self._suffixes, ) @@ -2136,11 +2138,19 @@ class CTE( name=self.name, recursive=self.recursive, nesting=self.nesting, - _restates=self._restates + (self,), + _restates=self, _prefixes=self._prefixes, _suffixes=self._suffixes, ) + def _get_reference_cte(self): + """ + A recursive CTE is updated to attach the recursive part. + Updated CTEs should still refer to the original CTE. + This function returns this reference identifier. + """ + return self._restates if self._restates is not None else self + class HasCTE(roles.HasCTERole): """Mixin that declares a class to include CTE support. |
