]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add CTE prefixes
authorMarat Sharafutdinov <decaz89@gmail.com>
Wed, 18 Dec 2019 15:39:59 +0000 (10:39 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 18 Dec 2019 22:54:10 +0000 (17:54 -0500)
Added support for prefixes to the :class:`.CTE` construct, to allow
support for Postgresql 12 "MATERIALIZED" and "NOT MATERIALIZED" phrases.
Pull request courtesy Marat Sharafutdinov.

Fixes: #5040
Closes: #5043
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5043
Pull-request-sha: d1b9059a0b6dae8dc2479ac670999b4af07908e0

Change-Id: I2e9cb5d7f85961ec98ee51965de5b3ec4a97be2f
(cherry picked from commit f8c562c4f8def3b104d157d9f39d0e11f7d9fd8d)

doc/build/changelog/unreleased_13/5040.rst [new file with mode: 0644]
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/selectable.py
test/sql/test_cte.py

diff --git a/doc/build/changelog/unreleased_13/5040.rst b/doc/build/changelog/unreleased_13/5040.rst
new file mode 100644 (file)
index 0000000..b1d45f0
--- /dev/null
@@ -0,0 +1,11 @@
+.. change::
+    :tags: usecase, postgresql
+    :tickets: 5040
+
+    Added support for prefixes to the :class:`.CTE` construct, to allow
+    support for Postgresql 12 "MATERIALIZED" and "NOT MATERIALIZED" phrases.
+    Pull request courtesy Marat Sharafutdinov.
+
+    .. seealso::
+
+        :meth:`.HasCTE.cte`
index 4dff9b25b2c4c34518002f6684906be1fae1e91f..3ce7bbf17917e0c594723ce4a447ce768ea23cea 100644 (file)
@@ -1694,8 +1694,11 @@ class SQLCompiler(Compiled):
                 if self.positional:
                     kwargs["positional_names"] = self.cte_positional[cte] = []
 
-                text += " AS \n" + cte.original._compiler_dispatch(
-                    self, asfrom=True, **kwargs
+                text += " AS %s\n%s" % (
+                    self._generate_prefixes(cte, cte._prefixes, **kwargs),
+                    cte.original._compiler_dispatch(
+                        self, asfrom=True, **kwargs
+                    ),
                 )
 
                 if cte._suffixes:
index c727b03b4ec02e573b57765e669b55770b717876..d4f636a95cdd329d7841b0bfc5f1a58778102a8e 100644 (file)
@@ -1502,7 +1502,7 @@ class TableSample(Alias):
             return functions.func.system(self.sampling)
 
 
-class CTE(Generative, HasSuffixes, Alias):
+class CTE(Generative, HasPrefixes, HasSuffixes, Alias):
     """Represent a Common Table Expression.
 
     The :class:`.CTE` object is obtained using the
@@ -1531,11 +1531,14 @@ class CTE(Generative, HasSuffixes, Alias):
         recursive=False,
         _cte_alias=None,
         _restates=frozenset(),
+        _prefixes=None,
         _suffixes=None,
     ):
         self.recursive = recursive
         self._cte_alias = _cte_alias
         self._restates = _restates
+        if _prefixes:
+            self._prefixes = _prefixes
         if _suffixes:
             self._suffixes = _suffixes
         super(CTE, self)._init(selectable, name=name)
@@ -1575,6 +1578,7 @@ class CTE(Generative, HasSuffixes, Alias):
             name=name,
             recursive=self.recursive,
             _cte_alias=self,
+            _prefixes=self._prefixes,
             _suffixes=self._suffixes,
         )
 
@@ -1584,6 +1588,7 @@ class CTE(Generative, HasSuffixes, Alias):
             name=self.name,
             recursive=self.recursive,
             _restates=self._restates.union([self]),
+            _prefixes=self._prefixes,
             _suffixes=self._suffixes,
         )
 
@@ -1593,6 +1598,7 @@ class CTE(Generative, HasSuffixes, Alias):
             name=self.name,
             recursive=self.recursive,
             _restates=self._restates.union([self]),
+            _prefixes=self._prefixes,
             _suffixes=self._suffixes,
         )
 
@@ -1619,13 +1625,20 @@ class HasCTE(object):
         when combined with RETURNING, as well as a consumer of
         CTE rows.
 
+        .. versionchanged:: 1.1 Added support for UPDATE/INSERT/DELETE as
+           CTE, CTEs added to UPDATE/INSERT/DELETE.
+
         SQLAlchemy detects :class:`.CTE` objects, which are treated
         similarly to :class:`.Alias` objects, as special elements
         to be delivered to the FROM clause of the statement as well
         as to a WITH clause at the top of the statement.
 
-        .. versionchanged:: 1.1 Added support for UPDATE/INSERT/DELETE as
-           CTE, CTEs added to UPDATE/INSERT/DELETE.
+        For special prefixes such as PostgreSQL "MATERIALIZED" and
+        "NOT MATERIALIZED", the :meth:`.CTE.prefix_with` method may be
+        used to establish these.
+
+        .. versionchanged:: 1.3.13 Added support for prefixes.
+           In particular - MATERIALIZED and NOT MATERIALIZED.
 
         :param name: name given to the common table expression.  Like
          :meth:`._FromClause.alias`, the name can be left as ``None``
index 7008bc1cca5631718be3130619e456ac37f807e8..cb6fc54279152b5be8e825050eb4ab478381a79e 100644 (file)
@@ -899,6 +899,28 @@ class CTETest(fixtures.TestBase, AssertsCompiledSQL):
             'ON anon_1."order" = "order"."order"',
         )
 
+    def test_prefixes(self):
+        orders = table("order", column("order"))
+        s = select([orders.c.order]).cte("regional_sales")
+        s = s.prefix_with("NOT MATERIALIZED", dialect="postgresql")
+        stmt = select([orders]).where(orders.c.order > s.c.order)
+
+        self.assert_compile(
+            stmt,
+            'WITH regional_sales AS (SELECT "order"."order" AS "order" '
+            'FROM "order") SELECT "order"."order" FROM "order", '
+            'regional_sales WHERE "order"."order" > regional_sales."order"',
+        )
+
+        self.assert_compile(
+            stmt,
+            "WITH regional_sales AS NOT MATERIALIZED "
+            '(SELECT "order"."order" AS "order" '
+            'FROM "order") SELECT "order"."order" FROM "order", '
+            'regional_sales WHERE "order"."order" > regional_sales."order"',
+            dialect="postgresql",
+        )
+
     def test_suffixes(self):
         orders = table("order", column("order"))
         s = select([orders.c.order]).cte("regional_sales")