test_cte.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. # testing/suite/test_cte.py
  2. # Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. # mypy: ignore-errors
  8. from .. import fixtures
  9. from ..assertions import eq_
  10. from ..schema import Column
  11. from ..schema import Table
  12. from ... import ForeignKey
  13. from ... import Integer
  14. from ... import select
  15. from ... import String
  16. from ... import testing
  17. class CTETest(fixtures.TablesTest):
  18. __backend__ = True
  19. __requires__ = ("ctes",)
  20. run_inserts = "each"
  21. run_deletes = "each"
  22. @classmethod
  23. def define_tables(cls, metadata):
  24. Table(
  25. "some_table",
  26. metadata,
  27. Column("id", Integer, primary_key=True),
  28. Column("data", String(50)),
  29. Column("parent_id", ForeignKey("some_table.id")),
  30. )
  31. Table(
  32. "some_other_table",
  33. metadata,
  34. Column("id", Integer, primary_key=True),
  35. Column("data", String(50)),
  36. Column("parent_id", Integer),
  37. )
  38. @classmethod
  39. def insert_data(cls, connection):
  40. connection.execute(
  41. cls.tables.some_table.insert(),
  42. [
  43. {"id": 1, "data": "d1", "parent_id": None},
  44. {"id": 2, "data": "d2", "parent_id": 1},
  45. {"id": 3, "data": "d3", "parent_id": 1},
  46. {"id": 4, "data": "d4", "parent_id": 3},
  47. {"id": 5, "data": "d5", "parent_id": 3},
  48. ],
  49. )
  50. def test_select_nonrecursive_round_trip(self, connection):
  51. some_table = self.tables.some_table
  52. cte = (
  53. select(some_table)
  54. .where(some_table.c.data.in_(["d2", "d3", "d4"]))
  55. .cte("some_cte")
  56. )
  57. result = connection.execute(
  58. select(cte.c.data).where(cte.c.data.in_(["d4", "d5"]))
  59. )
  60. eq_(result.fetchall(), [("d4",)])
  61. def test_select_recursive_round_trip(self, connection):
  62. some_table = self.tables.some_table
  63. cte = (
  64. select(some_table)
  65. .where(some_table.c.data.in_(["d2", "d3", "d4"]))
  66. .cte("some_cte", recursive=True)
  67. )
  68. cte_alias = cte.alias("c1")
  69. st1 = some_table.alias()
  70. # note that SQL Server requires this to be UNION ALL,
  71. # can't be UNION
  72. cte = cte.union_all(
  73. select(st1).where(st1.c.id == cte_alias.c.parent_id)
  74. )
  75. result = connection.execute(
  76. select(cte.c.data)
  77. .where(cte.c.data != "d2")
  78. .order_by(cte.c.data.desc())
  79. )
  80. eq_(
  81. result.fetchall(),
  82. [("d4",), ("d3",), ("d3",), ("d1",), ("d1",), ("d1",)],
  83. )
  84. def test_insert_from_select_round_trip(self, connection):
  85. some_table = self.tables.some_table
  86. some_other_table = self.tables.some_other_table
  87. cte = (
  88. select(some_table)
  89. .where(some_table.c.data.in_(["d2", "d3", "d4"]))
  90. .cte("some_cte")
  91. )
  92. connection.execute(
  93. some_other_table.insert().from_select(
  94. ["id", "data", "parent_id"], select(cte)
  95. )
  96. )
  97. eq_(
  98. connection.execute(
  99. select(some_other_table).order_by(some_other_table.c.id)
  100. ).fetchall(),
  101. [(2, "d2", 1), (3, "d3", 1), (4, "d4", 3)],
  102. )
  103. @testing.requires.ctes_with_update_delete
  104. @testing.requires.update_from
  105. def test_update_from_round_trip(self, connection):
  106. some_table = self.tables.some_table
  107. some_other_table = self.tables.some_other_table
  108. connection.execute(
  109. some_other_table.insert().from_select(
  110. ["id", "data", "parent_id"], select(some_table)
  111. )
  112. )
  113. cte = (
  114. select(some_table)
  115. .where(some_table.c.data.in_(["d2", "d3", "d4"]))
  116. .cte("some_cte")
  117. )
  118. connection.execute(
  119. some_other_table.update()
  120. .values(parent_id=5)
  121. .where(some_other_table.c.data == cte.c.data)
  122. )
  123. eq_(
  124. connection.execute(
  125. select(some_other_table).order_by(some_other_table.c.id)
  126. ).fetchall(),
  127. [
  128. (1, "d1", None),
  129. (2, "d2", 5),
  130. (3, "d3", 5),
  131. (4, "d4", 5),
  132. (5, "d5", 3),
  133. ],
  134. )
  135. @testing.requires.ctes_with_update_delete
  136. @testing.requires.delete_from
  137. def test_delete_from_round_trip(self, connection):
  138. some_table = self.tables.some_table
  139. some_other_table = self.tables.some_other_table
  140. connection.execute(
  141. some_other_table.insert().from_select(
  142. ["id", "data", "parent_id"], select(some_table)
  143. )
  144. )
  145. cte = (
  146. select(some_table)
  147. .where(some_table.c.data.in_(["d2", "d3", "d4"]))
  148. .cte("some_cte")
  149. )
  150. connection.execute(
  151. some_other_table.delete().where(
  152. some_other_table.c.data == cte.c.data
  153. )
  154. )
  155. eq_(
  156. connection.execute(
  157. select(some_other_table).order_by(some_other_table.c.id)
  158. ).fetchall(),
  159. [(1, "d1", None), (5, "d5", 3)],
  160. )
  161. @testing.requires.ctes_with_update_delete
  162. def test_delete_scalar_subq_round_trip(self, connection):
  163. some_table = self.tables.some_table
  164. some_other_table = self.tables.some_other_table
  165. connection.execute(
  166. some_other_table.insert().from_select(
  167. ["id", "data", "parent_id"], select(some_table)
  168. )
  169. )
  170. cte = (
  171. select(some_table)
  172. .where(some_table.c.data.in_(["d2", "d3", "d4"]))
  173. .cte("some_cte")
  174. )
  175. connection.execute(
  176. some_other_table.delete().where(
  177. some_other_table.c.data
  178. == select(cte.c.data)
  179. .where(cte.c.id == some_other_table.c.id)
  180. .scalar_subquery()
  181. )
  182. )
  183. eq_(
  184. connection.execute(
  185. select(some_other_table).order_by(some_other_table.c.id)
  186. ).fetchall(),
  187. [(1, "d1", None), (5, "d5", 3)],
  188. )