test_ddl.py 12 KB

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