]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Merge remote-tracking branch 'origin/master' into cte-nesting-support
authorEric Masseran <eric.masseran@gmail.com>
Fri, 16 Jul 2021 17:25:02 +0000 (19:25 +0200)
committerEric Masseran <eric.masseran@gmail.com>
Fri, 16 Jul 2021 17:25:02 +0000 (19:25 +0200)
* origin/master: (27 commits)
  reset key/name when TableValuedColumn is adapted
  limit None->null coercion to not occur with crud
  apply list() around weakkeydictionary iteration
  update case statement in dictlike-polymorphic
  Version 1.4.22 placeholder
  - 1.4.21
  Ensure alias traversal block works when adapt_from_selectables present
  typo
  changelog updates
  Adjust CTE recrusive col list to accommodate dupe col names
  Extract format_constraint truncation rules to ON CONFLICT
  labeling refactor
  Modernize tests - union
  implement independent CTEs
  Correct docs: pg8000 supports PostgreSQL UUID
  represent tablesample.sampling as FunctionElement in all cases
  repair schema_translate_map for schema type use cases
  implement deferred scalarobject history load
  add python 2.7 to pr workflow to help catch py3 only issues
  Docs: fixed typo in "Cascades"
  ...

1  2 
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/selectable.py
test/sql/test_cte.py

Simple merge
Simple merge
index 740ccc0ae70d9805adc40a4552f1fdd39f48ae7b,b6cf7f55e85336145328f9d0675acd7a122f5864..f6fcffbf0f966ae085f52525eaf92cff9c68c895
@@@ -2115,7 -2111,80 +2116,80 @@@ class HasCTE(roles.HasCTERole)
  
      """
  
 -    def cte(self, name=None, recursive=False):
+     _has_ctes_traverse_internals = [
+         ("_independent_ctes", InternalTraversal.dp_clauseelement_list),
+     ]
+     _independent_ctes = ()
+     @_generative
+     def add_cte(self, cte):
+         """Add a :class:`_sql.CTE` to this statement object that will be
+         independently rendered even if not referenced in the statement
+         otherwise.
+         This feature is useful for the use case of embedding a DML statement
+         such as an INSERT or UPDATE as a CTE inline with a primary statement
+         that may draw from its results indirectly; while PostgreSQL is known
+         to support this usage, it may not be supported by other backends.
+         E.g.::
+             from sqlalchemy import table, column, select
+             t = table('t', column('c1'), column('c2'))
+             ins = t.insert().values({"c1": "x", "c2": "y"}).cte()
+             stmt = select(t).add_cte(ins)
+         Would render::
+             WITH anon_1 AS
+             (INSERT INTO t (c1, c2) VALUES (:param_1, :param_2))
+             SELECT t.c1, t.c2
+             FROM t
+         Above, the "anon_1" CTE is not referred towards in the SELECT
+         statement, however still accomplishes the task of running an INSERT
+         statement.
+         Similarly in a DML-related context, using the PostgreSQL
+         :class:`_postgresql.Insert` construct to generate an "upsert"::
+             from sqlalchemy import table, column
+             from sqlalchemy.dialects.postgresql import insert
+             t = table("t", column("c1"), column("c2"))
+             delete_statement_cte = (
+                 t.delete().where(t.c.c1 < 1).cte("deletions")
+             )
+             insert_stmt = insert(t).values({"c1": 1, "c2": 2})
+             update_statement = insert_stmt.on_conflict_do_update(
+                 index_elements=[t.c.c1],
+                 set_={
+                     "c1": insert_stmt.excluded.c1,
+                     "c2": insert_stmt.excluded.c2,
+                 },
+             ).add_cte(delete_statement_cte)
+             print(update_statement)
+         The above statement renders as::
+             WITH deletions AS
+             (DELETE FROM t WHERE t.c1 < %(c1_1)s)
+             INSERT INTO t (c1, c2) VALUES (%(c1)s, %(c2)s)
+             ON CONFLICT (c1) DO UPDATE SET c1 = excluded.c1, c2 = excluded.c2
+         .. versionadded:: 1.4.21
+         """
+         cte = coercions.expect(roles.IsCTERole, cte)
+         self._independent_ctes += (cte,)
 +    def cte(self, name=None, recursive=False, nesting=False):
          r"""Return a new :class:`_expression.CTE`,
          or Common Table Expression instance.
  
index b53a5f014b253ba739b8c843e7a055e70de5c8e7,f1d27aa8f174c6d73d7cad8bd2144ae5fc4b1030..e9773707baef94cfe8b1a283309035e0271bda28
@@@ -1,5 -1,9 +1,10 @@@
 +import functools
+ from sqlalchemy import Column
  from sqlalchemy import delete
+ from sqlalchemy import Integer
+ from sqlalchemy import LABEL_STYLE_TABLENAME_PLUS_COL
+ from sqlalchemy import MetaData
+ from sqlalchemy import Table
  from sqlalchemy import testing
  from sqlalchemy import text
  from sqlalchemy import update