test_ddl.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. # testing/suite/test_ddl.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. import random
  8. from . import testing
  9. from .. import config
  10. from .. import fixtures
  11. from .. import util
  12. from ..assertions import eq_
  13. from ..assertions import is_false
  14. from ..assertions import is_true
  15. from ..config import requirements
  16. from ..schema import Table
  17. from ... import CheckConstraint
  18. from ... import Column
  19. from ... import ForeignKeyConstraint
  20. from ... import Index
  21. from ... import inspect
  22. from ... import Integer
  23. from ... import schema
  24. from ... import String
  25. from ... import UniqueConstraint
  26. class TableDDLTest(fixtures.TestBase):
  27. __backend__ = True
  28. def _simple_fixture(self, schema=None):
  29. return Table(
  30. "test_table",
  31. self.metadata,
  32. Column("id", Integer, primary_key=True, autoincrement=False),
  33. Column("data", String(50)),
  34. schema=schema,
  35. )
  36. def _underscore_fixture(self):
  37. return Table(
  38. "_test_table",
  39. self.metadata,
  40. Column("id", Integer, primary_key=True, autoincrement=False),
  41. Column("_data", String(50)),
  42. )
  43. def _table_index_fixture(self, schema=None):
  44. table = self._simple_fixture(schema=schema)
  45. idx = Index("test_index", table.c.data)
  46. return table, idx
  47. def _simple_roundtrip(self, table):
  48. with config.db.begin() as conn:
  49. conn.execute(table.insert().values((1, "some data")))
  50. result = conn.execute(table.select())
  51. eq_(result.first(), (1, "some data"))
  52. @requirements.create_table
  53. @util.provide_metadata
  54. def test_create_table(self):
  55. table = self._simple_fixture()
  56. table.create(config.db, checkfirst=False)
  57. self._simple_roundtrip(table)
  58. @requirements.create_table
  59. @requirements.schemas
  60. @util.provide_metadata
  61. def test_create_table_schema(self):
  62. table = self._simple_fixture(schema=config.test_schema)
  63. table.create(config.db, checkfirst=False)
  64. self._simple_roundtrip(table)
  65. @requirements.drop_table
  66. @util.provide_metadata
  67. def test_drop_table(self):
  68. table = self._simple_fixture()
  69. table.create(config.db, checkfirst=False)
  70. table.drop(config.db, checkfirst=False)
  71. @requirements.create_table
  72. @util.provide_metadata
  73. def test_underscore_names(self):
  74. table = self._underscore_fixture()
  75. table.create(config.db, checkfirst=False)
  76. self._simple_roundtrip(table)
  77. @requirements.comment_reflection
  78. @util.provide_metadata
  79. def test_add_table_comment(self, connection):
  80. table = self._simple_fixture()
  81. table.create(connection, checkfirst=False)
  82. table.comment = "a comment"
  83. connection.execute(schema.SetTableComment(table))
  84. eq_(
  85. inspect(connection).get_table_comment("test_table"),
  86. {"text": "a comment"},
  87. )
  88. @requirements.comment_reflection
  89. @util.provide_metadata
  90. def test_drop_table_comment(self, connection):
  91. table = self._simple_fixture()
  92. table.create(connection, checkfirst=False)
  93. table.comment = "a comment"
  94. connection.execute(schema.SetTableComment(table))
  95. connection.execute(schema.DropTableComment(table))
  96. eq_(
  97. inspect(connection).get_table_comment("test_table"), {"text": None}
  98. )
  99. @requirements.table_ddl_if_exists
  100. @util.provide_metadata
  101. def test_create_table_if_not_exists(self, connection):
  102. table = self._simple_fixture()
  103. connection.execute(schema.CreateTable(table, if_not_exists=True))
  104. is_true(inspect(connection).has_table("test_table"))
  105. connection.execute(schema.CreateTable(table, if_not_exists=True))
  106. @requirements.index_ddl_if_exists
  107. @util.provide_metadata
  108. def test_create_index_if_not_exists(self, connection):
  109. table, idx = self._table_index_fixture()
  110. connection.execute(schema.CreateTable(table, if_not_exists=True))
  111. is_true(inspect(connection).has_table("test_table"))
  112. is_false(
  113. "test_index"
  114. in [
  115. ix["name"]
  116. for ix in inspect(connection).get_indexes("test_table")
  117. ]
  118. )
  119. connection.execute(schema.CreateIndex(idx, if_not_exists=True))
  120. is_true(
  121. "test_index"
  122. in [
  123. ix["name"]
  124. for ix in inspect(connection).get_indexes("test_table")
  125. ]
  126. )
  127. connection.execute(schema.CreateIndex(idx, if_not_exists=True))
  128. @requirements.table_ddl_if_exists
  129. @util.provide_metadata
  130. def test_drop_table_if_exists(self, connection):
  131. table = self._simple_fixture()
  132. table.create(connection)
  133. is_true(inspect(connection).has_table("test_table"))
  134. connection.execute(schema.DropTable(table, if_exists=True))
  135. is_false(inspect(connection).has_table("test_table"))
  136. connection.execute(schema.DropTable(table, if_exists=True))
  137. @requirements.index_ddl_if_exists
  138. @util.provide_metadata
  139. def test_drop_index_if_exists(self, connection):
  140. table, idx = self._table_index_fixture()
  141. table.create(connection)
  142. is_true(
  143. "test_index"
  144. in [
  145. ix["name"]
  146. for ix in inspect(connection).get_indexes("test_table")
  147. ]
  148. )
  149. connection.execute(schema.DropIndex(idx, if_exists=True))
  150. is_false(
  151. "test_index"
  152. in [
  153. ix["name"]
  154. for ix in inspect(connection).get_indexes("test_table")
  155. ]
  156. )
  157. connection.execute(schema.DropIndex(idx, if_exists=True))
  158. class FutureTableDDLTest(fixtures.FutureEngineMixin, TableDDLTest):
  159. pass
  160. class LongNameBlowoutTest(fixtures.TestBase):
  161. """test the creation of a variety of DDL structures and ensure
  162. label length limits pass on backends
  163. """
  164. __backend__ = True
  165. def fk(self, metadata, connection):
  166. convention = {
  167. "fk": "foreign_key_%(table_name)s_"
  168. "%(column_0_N_name)s_"
  169. "%(referred_table_name)s_"
  170. + (
  171. "_".join(
  172. "".join(random.choice("abcdef") for j in range(20))
  173. for i in range(10)
  174. )
  175. ),
  176. }
  177. metadata.naming_convention = convention
  178. Table(
  179. "a_things_with_stuff",
  180. metadata,
  181. Column("id_long_column_name", Integer, primary_key=True),
  182. test_needs_fk=True,
  183. )
  184. cons = ForeignKeyConstraint(
  185. ["aid"], ["a_things_with_stuff.id_long_column_name"]
  186. )
  187. Table(
  188. "b_related_things_of_value",
  189. metadata,
  190. Column(
  191. "aid",
  192. ),
  193. cons,
  194. test_needs_fk=True,
  195. )
  196. actual_name = cons.name
  197. metadata.create_all(connection)
  198. if testing.requires.foreign_key_constraint_name_reflection.enabled:
  199. insp = inspect(connection)
  200. fks = insp.get_foreign_keys("b_related_things_of_value")
  201. reflected_name = fks[0]["name"]
  202. return actual_name, reflected_name
  203. else:
  204. return actual_name, None
  205. def pk(self, metadata, connection):
  206. convention = {
  207. "pk": "primary_key_%(table_name)s_"
  208. "%(column_0_N_name)s"
  209. + (
  210. "_".join(
  211. "".join(random.choice("abcdef") for j in range(30))
  212. for i in range(10)
  213. )
  214. ),
  215. }
  216. metadata.naming_convention = convention
  217. a = Table(
  218. "a_things_with_stuff",
  219. metadata,
  220. Column("id_long_column_name", Integer, primary_key=True),
  221. Column("id_another_long_name", Integer, primary_key=True),
  222. )
  223. cons = a.primary_key
  224. actual_name = cons.name
  225. metadata.create_all(connection)
  226. insp = inspect(connection)
  227. pk = insp.get_pk_constraint("a_things_with_stuff")
  228. reflected_name = pk["name"]
  229. return actual_name, reflected_name
  230. def ix(self, metadata, connection):
  231. convention = {
  232. "ix": "index_%(table_name)s_"
  233. "%(column_0_N_name)s"
  234. + (
  235. "_".join(
  236. "".join(random.choice("abcdef") for j in range(30))
  237. for i in range(10)
  238. )
  239. ),
  240. }
  241. metadata.naming_convention = convention
  242. a = Table(
  243. "a_things_with_stuff",
  244. metadata,
  245. Column("id_long_column_name", Integer, primary_key=True),
  246. Column("id_another_long_name", Integer),
  247. )
  248. cons = Index(None, a.c.id_long_column_name, a.c.id_another_long_name)
  249. actual_name = cons.name
  250. metadata.create_all(connection)
  251. insp = inspect(connection)
  252. ix = insp.get_indexes("a_things_with_stuff")
  253. reflected_name = ix[0]["name"]
  254. return actual_name, reflected_name
  255. def uq(self, metadata, connection):
  256. convention = {
  257. "uq": "unique_constraint_%(table_name)s_"
  258. "%(column_0_N_name)s"
  259. + (
  260. "_".join(
  261. "".join(random.choice("abcdef") for j in range(30))
  262. for i in range(10)
  263. )
  264. ),
  265. }
  266. metadata.naming_convention = convention
  267. cons = UniqueConstraint("id_long_column_name", "id_another_long_name")
  268. Table(
  269. "a_things_with_stuff",
  270. metadata,
  271. Column("id_long_column_name", Integer, primary_key=True),
  272. Column("id_another_long_name", Integer),
  273. cons,
  274. )
  275. actual_name = cons.name
  276. metadata.create_all(connection)
  277. insp = inspect(connection)
  278. uq = insp.get_unique_constraints("a_things_with_stuff")
  279. reflected_name = uq[0]["name"]
  280. return actual_name, reflected_name
  281. def ck(self, metadata, connection):
  282. convention = {
  283. "ck": "check_constraint_%(table_name)s"
  284. + (
  285. "_".join(
  286. "".join(random.choice("abcdef") for j in range(30))
  287. for i in range(10)
  288. )
  289. ),
  290. }
  291. metadata.naming_convention = convention
  292. cons = CheckConstraint("some_long_column_name > 5")
  293. Table(
  294. "a_things_with_stuff",
  295. metadata,
  296. Column("id_long_column_name", Integer, primary_key=True),
  297. Column("some_long_column_name", Integer),
  298. cons,
  299. )
  300. actual_name = cons.name
  301. metadata.create_all(connection)
  302. insp = inspect(connection)
  303. ck = insp.get_check_constraints("a_things_with_stuff")
  304. reflected_name = ck[0]["name"]
  305. return actual_name, reflected_name
  306. @testing.combinations(
  307. ("fk",),
  308. ("pk",),
  309. ("ix",),
  310. ("ck", testing.requires.check_constraint_reflection.as_skips()),
  311. ("uq", testing.requires.unique_constraint_reflection.as_skips()),
  312. argnames="type_",
  313. )
  314. def test_long_convention_name(self, type_, metadata, connection):
  315. actual_name, reflected_name = getattr(self, type_)(
  316. metadata, connection
  317. )
  318. assert len(actual_name) > 255
  319. if reflected_name is not None:
  320. overlap = actual_name[0 : len(reflected_name)]
  321. if len(overlap) < len(actual_name):
  322. eq_(overlap[0:-5], reflected_name[0 : len(overlap) - 5])
  323. else:
  324. eq_(overlap, reflected_name)
  325. __all__ = ("TableDDLTest", "FutureTableDDLTest", "LongNameBlowoutTest")