summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/selectable.py
diff options
context:
space:
mode:
authorEric Masseran <eric.masseran@gmail.com>2021-10-08 10:02:58 -0400
committermike bayer <mike_mp@zzzcomputing.com>2021-10-12 22:46:57 +0000
commitee9b8836a160484733baa556c5d3ade4810aa999 (patch)
tree623c4fa6e17d2366934b931a9695f12dc1a34e9f /lib/sqlalchemy/sql/selectable.py
parentde9db9940fbcf32ccd93169d2ed6aa874869b84d (diff)
downloadsqlalchemy-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.py18
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.