test_autogen_fks.py 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
  1. from sqlalchemy import Column
  2. from sqlalchemy import ForeignKeyConstraint
  3. from sqlalchemy import Integer
  4. from sqlalchemy import MetaData
  5. from sqlalchemy import String
  6. from sqlalchemy import Table
  7. from ._autogen_fixtures import AutogenFixtureTest
  8. from ...testing import combinations
  9. from ...testing import config
  10. from ...testing import eq_
  11. from ...testing import mock
  12. from ...testing import TestBase
  13. class AutogenerateForeignKeysTest(AutogenFixtureTest, TestBase):
  14. __backend__ = True
  15. __requires__ = ("foreign_key_constraint_reflection",)
  16. def test_remove_fk(self):
  17. m1 = MetaData()
  18. m2 = MetaData()
  19. Table(
  20. "some_table",
  21. m1,
  22. Column("test", String(10), primary_key=True),
  23. )
  24. Table(
  25. "user",
  26. m1,
  27. Column("id", Integer, primary_key=True),
  28. Column("name", String(50), nullable=False),
  29. Column("a1", String(10), server_default="x"),
  30. Column("test2", String(10)),
  31. ForeignKeyConstraint(["test2"], ["some_table.test"]),
  32. )
  33. Table(
  34. "some_table",
  35. m2,
  36. Column("test", String(10), primary_key=True),
  37. )
  38. Table(
  39. "user",
  40. m2,
  41. Column("id", Integer, primary_key=True),
  42. Column("name", String(50), nullable=False),
  43. Column("a1", String(10), server_default="x"),
  44. Column("test2", String(10)),
  45. )
  46. diffs = self._fixture(m1, m2)
  47. self._assert_fk_diff(
  48. diffs[0],
  49. "remove_fk",
  50. "user",
  51. ["test2"],
  52. "some_table",
  53. ["test"],
  54. conditional_name="servergenerated",
  55. )
  56. def test_add_fk(self):
  57. m1 = MetaData()
  58. m2 = MetaData()
  59. Table(
  60. "some_table",
  61. m1,
  62. Column("id", Integer, primary_key=True),
  63. Column("test", String(10)),
  64. )
  65. Table(
  66. "user",
  67. m1,
  68. Column("id", Integer, primary_key=True),
  69. Column("name", String(50), nullable=False),
  70. Column("a1", String(10), server_default="x"),
  71. Column("test2", String(10)),
  72. )
  73. Table(
  74. "some_table",
  75. m2,
  76. Column("id", Integer, primary_key=True),
  77. Column("test", String(10)),
  78. )
  79. Table(
  80. "user",
  81. m2,
  82. Column("id", Integer, primary_key=True),
  83. Column("name", String(50), nullable=False),
  84. Column("a1", String(10), server_default="x"),
  85. Column("test2", String(10)),
  86. ForeignKeyConstraint(["test2"], ["some_table.test"]),
  87. )
  88. diffs = self._fixture(m1, m2)
  89. self._assert_fk_diff(
  90. diffs[0], "add_fk", "user", ["test2"], "some_table", ["test"]
  91. )
  92. def test_no_change(self):
  93. m1 = MetaData()
  94. m2 = MetaData()
  95. Table(
  96. "some_table",
  97. m1,
  98. Column("id", Integer, primary_key=True),
  99. Column("test", String(10)),
  100. )
  101. Table(
  102. "user",
  103. m1,
  104. Column("id", Integer, primary_key=True),
  105. Column("name", String(50), nullable=False),
  106. Column("a1", String(10), server_default="x"),
  107. Column("test2", Integer),
  108. ForeignKeyConstraint(["test2"], ["some_table.id"]),
  109. )
  110. Table(
  111. "some_table",
  112. m2,
  113. Column("id", Integer, primary_key=True),
  114. Column("test", String(10)),
  115. )
  116. Table(
  117. "user",
  118. m2,
  119. Column("id", Integer, primary_key=True),
  120. Column("name", String(50), nullable=False),
  121. Column("a1", String(10), server_default="x"),
  122. Column("test2", Integer),
  123. ForeignKeyConstraint(["test2"], ["some_table.id"]),
  124. )
  125. diffs = self._fixture(m1, m2)
  126. eq_(diffs, [])
  127. def test_no_change_composite_fk(self):
  128. m1 = MetaData()
  129. m2 = MetaData()
  130. Table(
  131. "some_table",
  132. m1,
  133. Column("id_1", String(10), primary_key=True),
  134. Column("id_2", String(10), primary_key=True),
  135. )
  136. Table(
  137. "user",
  138. m1,
  139. Column("id", Integer, primary_key=True),
  140. Column("name", String(50), nullable=False),
  141. Column("a1", String(10), server_default="x"),
  142. Column("other_id_1", String(10)),
  143. Column("other_id_2", String(10)),
  144. ForeignKeyConstraint(
  145. ["other_id_1", "other_id_2"],
  146. ["some_table.id_1", "some_table.id_2"],
  147. ),
  148. )
  149. Table(
  150. "some_table",
  151. m2,
  152. Column("id_1", String(10), primary_key=True),
  153. Column("id_2", String(10), primary_key=True),
  154. )
  155. Table(
  156. "user",
  157. m2,
  158. Column("id", Integer, primary_key=True),
  159. Column("name", String(50), nullable=False),
  160. Column("a1", String(10), server_default="x"),
  161. Column("other_id_1", String(10)),
  162. Column("other_id_2", String(10)),
  163. ForeignKeyConstraint(
  164. ["other_id_1", "other_id_2"],
  165. ["some_table.id_1", "some_table.id_2"],
  166. ),
  167. )
  168. diffs = self._fixture(m1, m2)
  169. eq_(diffs, [])
  170. def test_casing_convention_changed_so_put_drops_first(self):
  171. m1 = MetaData()
  172. m2 = MetaData()
  173. Table(
  174. "some_table",
  175. m1,
  176. Column("test", String(10), primary_key=True),
  177. )
  178. Table(
  179. "user",
  180. m1,
  181. Column("id", Integer, primary_key=True),
  182. Column("name", String(50), nullable=False),
  183. Column("a1", String(10), server_default="x"),
  184. Column("test2", String(10)),
  185. ForeignKeyConstraint(["test2"], ["some_table.test"], name="MyFK"),
  186. )
  187. Table(
  188. "some_table",
  189. m2,
  190. Column("test", String(10), primary_key=True),
  191. )
  192. # foreign key autogen currently does not take "name" into account,
  193. # so change the def just for the purposes of testing the
  194. # add/drop order for now.
  195. Table(
  196. "user",
  197. m2,
  198. Column("id", Integer, primary_key=True),
  199. Column("name", String(50), nullable=False),
  200. Column("a1", String(10), server_default="x"),
  201. Column("test2", String(10)),
  202. ForeignKeyConstraint(["a1"], ["some_table.test"], name="myfk"),
  203. )
  204. diffs = self._fixture(m1, m2)
  205. self._assert_fk_diff(
  206. diffs[0],
  207. "remove_fk",
  208. "user",
  209. ["test2"],
  210. "some_table",
  211. ["test"],
  212. name="MyFK" if config.requirements.fk_names.enabled else None,
  213. )
  214. self._assert_fk_diff(
  215. diffs[1],
  216. "add_fk",
  217. "user",
  218. ["a1"],
  219. "some_table",
  220. ["test"],
  221. name="myfk",
  222. )
  223. def test_add_composite_fk_with_name(self):
  224. m1 = MetaData()
  225. m2 = MetaData()
  226. Table(
  227. "some_table",
  228. m1,
  229. Column("id_1", String(10), primary_key=True),
  230. Column("id_2", String(10), primary_key=True),
  231. )
  232. Table(
  233. "user",
  234. m1,
  235. Column("id", Integer, primary_key=True),
  236. Column("name", String(50), nullable=False),
  237. Column("a1", String(10), server_default="x"),
  238. Column("other_id_1", String(10)),
  239. Column("other_id_2", String(10)),
  240. )
  241. Table(
  242. "some_table",
  243. m2,
  244. Column("id_1", String(10), primary_key=True),
  245. Column("id_2", String(10), primary_key=True),
  246. )
  247. Table(
  248. "user",
  249. m2,
  250. Column("id", Integer, primary_key=True),
  251. Column("name", String(50), nullable=False),
  252. Column("a1", String(10), server_default="x"),
  253. Column("other_id_1", String(10)),
  254. Column("other_id_2", String(10)),
  255. ForeignKeyConstraint(
  256. ["other_id_1", "other_id_2"],
  257. ["some_table.id_1", "some_table.id_2"],
  258. name="fk_test_name",
  259. ),
  260. )
  261. diffs = self._fixture(m1, m2)
  262. self._assert_fk_diff(
  263. diffs[0],
  264. "add_fk",
  265. "user",
  266. ["other_id_1", "other_id_2"],
  267. "some_table",
  268. ["id_1", "id_2"],
  269. name="fk_test_name",
  270. )
  271. @config.requirements.no_name_normalize
  272. def test_remove_composite_fk(self):
  273. m1 = MetaData()
  274. m2 = MetaData()
  275. Table(
  276. "some_table",
  277. m1,
  278. Column("id_1", String(10), primary_key=True),
  279. Column("id_2", String(10), primary_key=True),
  280. )
  281. Table(
  282. "user",
  283. m1,
  284. Column("id", Integer, primary_key=True),
  285. Column("name", String(50), nullable=False),
  286. Column("a1", String(10), server_default="x"),
  287. Column("other_id_1", String(10)),
  288. Column("other_id_2", String(10)),
  289. ForeignKeyConstraint(
  290. ["other_id_1", "other_id_2"],
  291. ["some_table.id_1", "some_table.id_2"],
  292. name="fk_test_name",
  293. ),
  294. )
  295. Table(
  296. "some_table",
  297. m2,
  298. Column("id_1", String(10), primary_key=True),
  299. Column("id_2", String(10), primary_key=True),
  300. )
  301. Table(
  302. "user",
  303. m2,
  304. Column("id", Integer, primary_key=True),
  305. Column("name", String(50), nullable=False),
  306. Column("a1", String(10), server_default="x"),
  307. Column("other_id_1", String(10)),
  308. Column("other_id_2", String(10)),
  309. )
  310. diffs = self._fixture(m1, m2)
  311. self._assert_fk_diff(
  312. diffs[0],
  313. "remove_fk",
  314. "user",
  315. ["other_id_1", "other_id_2"],
  316. "some_table",
  317. ["id_1", "id_2"],
  318. conditional_name="fk_test_name",
  319. )
  320. def test_add_fk_colkeys(self):
  321. m1 = MetaData()
  322. m2 = MetaData()
  323. Table(
  324. "some_table",
  325. m1,
  326. Column("id_1", String(10), primary_key=True),
  327. Column("id_2", String(10), primary_key=True),
  328. )
  329. Table(
  330. "user",
  331. m1,
  332. Column("id", Integer, primary_key=True),
  333. Column("other_id_1", String(10)),
  334. Column("other_id_2", String(10)),
  335. )
  336. Table(
  337. "some_table",
  338. m2,
  339. Column("id_1", String(10), key="tid1", primary_key=True),
  340. Column("id_2", String(10), key="tid2", primary_key=True),
  341. )
  342. Table(
  343. "user",
  344. m2,
  345. Column("id", Integer, primary_key=True),
  346. Column("other_id_1", String(10), key="oid1"),
  347. Column("other_id_2", String(10), key="oid2"),
  348. ForeignKeyConstraint(
  349. ["oid1", "oid2"],
  350. ["some_table.tid1", "some_table.tid2"],
  351. name="fk_test_name",
  352. ),
  353. )
  354. diffs = self._fixture(m1, m2)
  355. self._assert_fk_diff(
  356. diffs[0],
  357. "add_fk",
  358. "user",
  359. ["other_id_1", "other_id_2"],
  360. "some_table",
  361. ["id_1", "id_2"],
  362. name="fk_test_name",
  363. )
  364. def test_no_change_colkeys(self):
  365. m1 = MetaData()
  366. m2 = MetaData()
  367. Table(
  368. "some_table",
  369. m1,
  370. Column("id_1", String(10), primary_key=True),
  371. Column("id_2", String(10), primary_key=True),
  372. )
  373. Table(
  374. "user",
  375. m1,
  376. Column("id", Integer, primary_key=True),
  377. Column("other_id_1", String(10)),
  378. Column("other_id_2", String(10)),
  379. ForeignKeyConstraint(
  380. ["other_id_1", "other_id_2"],
  381. ["some_table.id_1", "some_table.id_2"],
  382. ),
  383. )
  384. Table(
  385. "some_table",
  386. m2,
  387. Column("id_1", String(10), key="tid1", primary_key=True),
  388. Column("id_2", String(10), key="tid2", primary_key=True),
  389. )
  390. Table(
  391. "user",
  392. m2,
  393. Column("id", Integer, primary_key=True),
  394. Column("other_id_1", String(10), key="oid1"),
  395. Column("other_id_2", String(10), key="oid2"),
  396. ForeignKeyConstraint(
  397. ["oid1", "oid2"], ["some_table.tid1", "some_table.tid2"]
  398. ),
  399. )
  400. diffs = self._fixture(m1, m2)
  401. eq_(diffs, [])
  402. class IncludeHooksTest(AutogenFixtureTest, TestBase):
  403. __backend__ = True
  404. __requires__ = ("fk_names",)
  405. @combinations(("object",), ("name",))
  406. @config.requirements.no_name_normalize
  407. def test_remove_connection_fk(self, hook_type):
  408. m1 = MetaData()
  409. m2 = MetaData()
  410. ref = Table(
  411. "ref",
  412. m1,
  413. Column("id", Integer, primary_key=True),
  414. )
  415. t1 = Table(
  416. "t",
  417. m1,
  418. Column("x", Integer),
  419. Column("y", Integer),
  420. )
  421. t1.append_constraint(
  422. ForeignKeyConstraint([t1.c.x], [ref.c.id], name="fk1")
  423. )
  424. t1.append_constraint(
  425. ForeignKeyConstraint([t1.c.y], [ref.c.id], name="fk2")
  426. )
  427. ref = Table(
  428. "ref",
  429. m2,
  430. Column("id", Integer, primary_key=True),
  431. )
  432. Table(
  433. "t",
  434. m2,
  435. Column("x", Integer),
  436. Column("y", Integer),
  437. )
  438. if hook_type == "object":
  439. def include_object(object_, name, type_, reflected, compare_to):
  440. return not (
  441. isinstance(object_, ForeignKeyConstraint)
  442. and type_ == "foreign_key_constraint"
  443. and reflected
  444. and name == "fk1"
  445. )
  446. diffs = self._fixture(m1, m2, object_filters=include_object)
  447. elif hook_type == "name":
  448. def include_name(name, type_, parent_names):
  449. if name == "fk1":
  450. if type_ == "index": # MariaDB thing
  451. return True
  452. eq_(type_, "foreign_key_constraint")
  453. eq_(
  454. parent_names,
  455. {
  456. "schema_name": None,
  457. "table_name": "t",
  458. "schema_qualified_table_name": "t",
  459. },
  460. )
  461. return False
  462. else:
  463. return True
  464. diffs = self._fixture(m1, m2, name_filters=include_name)
  465. self._assert_fk_diff(
  466. diffs[0],
  467. "remove_fk",
  468. "t",
  469. ["y"],
  470. "ref",
  471. ["id"],
  472. conditional_name="fk2",
  473. )
  474. eq_(len(diffs), 1)
  475. def test_add_metadata_fk(self):
  476. m1 = MetaData()
  477. m2 = MetaData()
  478. Table(
  479. "ref",
  480. m1,
  481. Column("id", Integer, primary_key=True),
  482. )
  483. Table(
  484. "t",
  485. m1,
  486. Column("x", Integer),
  487. Column("y", Integer),
  488. )
  489. ref = Table(
  490. "ref",
  491. m2,
  492. Column("id", Integer, primary_key=True),
  493. )
  494. t2 = Table(
  495. "t",
  496. m2,
  497. Column("x", Integer),
  498. Column("y", Integer),
  499. )
  500. t2.append_constraint(
  501. ForeignKeyConstraint([t2.c.x], [ref.c.id], name="fk1")
  502. )
  503. t2.append_constraint(
  504. ForeignKeyConstraint([t2.c.y], [ref.c.id], name="fk2")
  505. )
  506. def include_object(object_, name, type_, reflected, compare_to):
  507. return not (
  508. isinstance(object_, ForeignKeyConstraint)
  509. and type_ == "foreign_key_constraint"
  510. and not reflected
  511. and name == "fk1"
  512. )
  513. diffs = self._fixture(m1, m2, object_filters=include_object)
  514. self._assert_fk_diff(
  515. diffs[0], "add_fk", "t", ["y"], "ref", ["id"], name="fk2"
  516. )
  517. eq_(len(diffs), 1)
  518. @combinations(("object",), ("name",))
  519. @config.requirements.no_name_normalize
  520. def test_change_fk(self, hook_type):
  521. m1 = MetaData()
  522. m2 = MetaData()
  523. r1a = Table(
  524. "ref_a",
  525. m1,
  526. Column("a", Integer, primary_key=True),
  527. )
  528. Table(
  529. "ref_b",
  530. m1,
  531. Column("a", Integer, primary_key=True),
  532. Column("b", Integer, primary_key=True),
  533. )
  534. t1 = Table(
  535. "t",
  536. m1,
  537. Column("x", Integer),
  538. Column("y", Integer),
  539. Column("z", Integer),
  540. )
  541. t1.append_constraint(
  542. ForeignKeyConstraint([t1.c.x], [r1a.c.a], name="fk1")
  543. )
  544. t1.append_constraint(
  545. ForeignKeyConstraint([t1.c.y], [r1a.c.a], name="fk2")
  546. )
  547. Table(
  548. "ref_a",
  549. m2,
  550. Column("a", Integer, primary_key=True),
  551. )
  552. r2b = Table(
  553. "ref_b",
  554. m2,
  555. Column("a", Integer, primary_key=True),
  556. Column("b", Integer, primary_key=True),
  557. )
  558. t2 = Table(
  559. "t",
  560. m2,
  561. Column("x", Integer),
  562. Column("y", Integer),
  563. Column("z", Integer),
  564. )
  565. t2.append_constraint(
  566. ForeignKeyConstraint(
  567. [t2.c.x, t2.c.z], [r2b.c.a, r2b.c.b], name="fk1"
  568. )
  569. )
  570. t2.append_constraint(
  571. ForeignKeyConstraint(
  572. [t2.c.y, t2.c.z], [r2b.c.a, r2b.c.b], name="fk2"
  573. )
  574. )
  575. if hook_type == "object":
  576. def include_object(object_, name, type_, reflected, compare_to):
  577. return not (
  578. isinstance(object_, ForeignKeyConstraint)
  579. and type_ == "foreign_key_constraint"
  580. and name == "fk1"
  581. )
  582. diffs = self._fixture(m1, m2, object_filters=include_object)
  583. elif hook_type == "name":
  584. def include_name(name, type_, parent_names):
  585. if type_ == "index":
  586. return True # MariaDB thing
  587. if name == "fk1":
  588. eq_(type_, "foreign_key_constraint")
  589. eq_(
  590. parent_names,
  591. {
  592. "schema_name": None,
  593. "table_name": "t",
  594. "schema_qualified_table_name": "t",
  595. },
  596. )
  597. return False
  598. else:
  599. return True
  600. diffs = self._fixture(m1, m2, name_filters=include_name)
  601. if hook_type == "object":
  602. self._assert_fk_diff(
  603. diffs[0], "remove_fk", "t", ["y"], "ref_a", ["a"], name="fk2"
  604. )
  605. self._assert_fk_diff(
  606. diffs[1],
  607. "add_fk",
  608. "t",
  609. ["y", "z"],
  610. "ref_b",
  611. ["a", "b"],
  612. name="fk2",
  613. )
  614. eq_(len(diffs), 2)
  615. elif hook_type == "name":
  616. eq_(
  617. {(d[0], d[1].name) for d in diffs},
  618. {("add_fk", "fk2"), ("add_fk", "fk1"), ("remove_fk", "fk2")},
  619. )
  620. class AutogenerateFKOptionsTest(AutogenFixtureTest, TestBase):
  621. __backend__ = True
  622. def _fk_opts_fixture(self, old_opts, new_opts):
  623. m1 = MetaData()
  624. m2 = MetaData()
  625. Table(
  626. "some_table",
  627. m1,
  628. Column("id", Integer, primary_key=True),
  629. Column("test", String(10)),
  630. )
  631. Table(
  632. "user",
  633. m1,
  634. Column("id", Integer, primary_key=True),
  635. Column("name", String(50), nullable=False),
  636. Column("tid", Integer),
  637. ForeignKeyConstraint(["tid"], ["some_table.id"], **old_opts),
  638. )
  639. Table(
  640. "some_table",
  641. m2,
  642. Column("id", Integer, primary_key=True),
  643. Column("test", String(10)),
  644. )
  645. Table(
  646. "user",
  647. m2,
  648. Column("id", Integer, primary_key=True),
  649. Column("name", String(50), nullable=False),
  650. Column("tid", Integer),
  651. ForeignKeyConstraint(["tid"], ["some_table.id"], **new_opts),
  652. )
  653. return self._fixture(m1, m2)
  654. @config.requirements.fk_ondelete_is_reflected
  655. def test_add_ondelete(self):
  656. diffs = self._fk_opts_fixture({}, {"ondelete": "cascade"})
  657. self._assert_fk_diff(
  658. diffs[0],
  659. "remove_fk",
  660. "user",
  661. ["tid"],
  662. "some_table",
  663. ["id"],
  664. ondelete=None,
  665. conditional_name="servergenerated",
  666. )
  667. self._assert_fk_diff(
  668. diffs[1],
  669. "add_fk",
  670. "user",
  671. ["tid"],
  672. "some_table",
  673. ["id"],
  674. ondelete="cascade",
  675. )
  676. @config.requirements.fk_ondelete_is_reflected
  677. def test_remove_ondelete(self):
  678. diffs = self._fk_opts_fixture({"ondelete": "CASCADE"}, {})
  679. self._assert_fk_diff(
  680. diffs[0],
  681. "remove_fk",
  682. "user",
  683. ["tid"],
  684. "some_table",
  685. ["id"],
  686. ondelete="CASCADE",
  687. conditional_name="servergenerated",
  688. )
  689. self._assert_fk_diff(
  690. diffs[1],
  691. "add_fk",
  692. "user",
  693. ["tid"],
  694. "some_table",
  695. ["id"],
  696. ondelete=None,
  697. )
  698. def test_nochange_ondelete(self):
  699. """test case sensitivity"""
  700. diffs = self._fk_opts_fixture(
  701. {"ondelete": "caSCAde"}, {"ondelete": "CasCade"}
  702. )
  703. eq_(diffs, [])
  704. @config.requirements.fk_onupdate_is_reflected
  705. def test_add_onupdate(self):
  706. diffs = self._fk_opts_fixture({}, {"onupdate": "cascade"})
  707. self._assert_fk_diff(
  708. diffs[0],
  709. "remove_fk",
  710. "user",
  711. ["tid"],
  712. "some_table",
  713. ["id"],
  714. onupdate=None,
  715. conditional_name="servergenerated",
  716. )
  717. self._assert_fk_diff(
  718. diffs[1],
  719. "add_fk",
  720. "user",
  721. ["tid"],
  722. "some_table",
  723. ["id"],
  724. onupdate="cascade",
  725. )
  726. @config.requirements.fk_onupdate_is_reflected
  727. def test_remove_onupdate(self):
  728. diffs = self._fk_opts_fixture({"onupdate": "CASCADE"}, {})
  729. self._assert_fk_diff(
  730. diffs[0],
  731. "remove_fk",
  732. "user",
  733. ["tid"],
  734. "some_table",
  735. ["id"],
  736. onupdate="CASCADE",
  737. conditional_name="servergenerated",
  738. )
  739. self._assert_fk_diff(
  740. diffs[1],
  741. "add_fk",
  742. "user",
  743. ["tid"],
  744. "some_table",
  745. ["id"],
  746. onupdate=None,
  747. )
  748. @config.requirements.fk_onupdate
  749. def test_nochange_onupdate(self):
  750. """test case sensitivity"""
  751. diffs = self._fk_opts_fixture(
  752. {"onupdate": "caSCAde"}, {"onupdate": "CasCade"}
  753. )
  754. eq_(diffs, [])
  755. @config.requirements.fk_ondelete_restrict
  756. def test_nochange_ondelete_restrict(self):
  757. """test the RESTRICT option which MySQL doesn't report on"""
  758. diffs = self._fk_opts_fixture(
  759. {"ondelete": "restrict"}, {"ondelete": "restrict"}
  760. )
  761. eq_(diffs, [])
  762. @config.requirements.fk_onupdate_restrict
  763. def test_nochange_onupdate_restrict(self):
  764. """test the RESTRICT option which MySQL doesn't report on"""
  765. diffs = self._fk_opts_fixture(
  766. {"onupdate": "restrict"}, {"onupdate": "restrict"}
  767. )
  768. eq_(diffs, [])
  769. @config.requirements.fk_ondelete_noaction
  770. def test_nochange_ondelete_noaction(self):
  771. """test the NO ACTION option which generally comes back as None"""
  772. diffs = self._fk_opts_fixture(
  773. {"ondelete": "no action"}, {"ondelete": "no action"}
  774. )
  775. eq_(diffs, [])
  776. @config.requirements.fk_onupdate
  777. def test_nochange_onupdate_noaction(self):
  778. """test the NO ACTION option which generally comes back as None"""
  779. diffs = self._fk_opts_fixture(
  780. {"onupdate": "no action"}, {"onupdate": "no action"}
  781. )
  782. eq_(diffs, [])
  783. @config.requirements.fk_ondelete_restrict
  784. def test_change_ondelete_from_restrict(self):
  785. """test the RESTRICT option which MySQL doesn't report on"""
  786. # note that this is impossible to detect if we change
  787. # from RESTRICT to NO ACTION on MySQL.
  788. diffs = self._fk_opts_fixture(
  789. {"ondelete": "restrict"}, {"ondelete": "cascade"}
  790. )
  791. self._assert_fk_diff(
  792. diffs[0],
  793. "remove_fk",
  794. "user",
  795. ["tid"],
  796. "some_table",
  797. ["id"],
  798. onupdate=None,
  799. ondelete=mock.ANY, # MySQL reports None, PG reports RESTRICT
  800. conditional_name="servergenerated",
  801. )
  802. self._assert_fk_diff(
  803. diffs[1],
  804. "add_fk",
  805. "user",
  806. ["tid"],
  807. "some_table",
  808. ["id"],
  809. onupdate=None,
  810. ondelete="cascade",
  811. )
  812. @config.requirements.fk_ondelete_restrict
  813. def test_change_onupdate_from_restrict(self):
  814. """test the RESTRICT option which MySQL doesn't report on"""
  815. # note that this is impossible to detect if we change
  816. # from RESTRICT to NO ACTION on MySQL.
  817. diffs = self._fk_opts_fixture(
  818. {"onupdate": "restrict"}, {"onupdate": "cascade"}
  819. )
  820. self._assert_fk_diff(
  821. diffs[0],
  822. "remove_fk",
  823. "user",
  824. ["tid"],
  825. "some_table",
  826. ["id"],
  827. onupdate=mock.ANY, # MySQL reports None, PG reports RESTRICT
  828. ondelete=None,
  829. conditional_name="servergenerated",
  830. )
  831. self._assert_fk_diff(
  832. diffs[1],
  833. "add_fk",
  834. "user",
  835. ["tid"],
  836. "some_table",
  837. ["id"],
  838. onupdate="cascade",
  839. ondelete=None,
  840. )
  841. @config.requirements.fk_ondelete_is_reflected
  842. @config.requirements.fk_onupdate_is_reflected
  843. def test_ondelete_onupdate_combo(self):
  844. diffs = self._fk_opts_fixture(
  845. {"onupdate": "CASCADE", "ondelete": "SET NULL"},
  846. {"onupdate": "RESTRICT", "ondelete": "RESTRICT"},
  847. )
  848. self._assert_fk_diff(
  849. diffs[0],
  850. "remove_fk",
  851. "user",
  852. ["tid"],
  853. "some_table",
  854. ["id"],
  855. onupdate="CASCADE",
  856. ondelete="SET NULL",
  857. conditional_name="servergenerated",
  858. )
  859. self._assert_fk_diff(
  860. diffs[1],
  861. "add_fk",
  862. "user",
  863. ["tid"],
  864. "some_table",
  865. ["id"],
  866. onupdate="RESTRICT",
  867. ondelete="RESTRICT",
  868. )
  869. @config.requirements.fk_initially
  870. def test_add_initially_deferred(self):
  871. diffs = self._fk_opts_fixture({}, {"initially": "deferred"})
  872. self._assert_fk_diff(
  873. diffs[0],
  874. "remove_fk",
  875. "user",
  876. ["tid"],
  877. "some_table",
  878. ["id"],
  879. initially=None,
  880. conditional_name="servergenerated",
  881. )
  882. self._assert_fk_diff(
  883. diffs[1],
  884. "add_fk",
  885. "user",
  886. ["tid"],
  887. "some_table",
  888. ["id"],
  889. initially="deferred",
  890. )
  891. @config.requirements.fk_initially
  892. def test_remove_initially_deferred(self):
  893. diffs = self._fk_opts_fixture({"initially": "deferred"}, {})
  894. self._assert_fk_diff(
  895. diffs[0],
  896. "remove_fk",
  897. "user",
  898. ["tid"],
  899. "some_table",
  900. ["id"],
  901. initially="DEFERRED",
  902. deferrable=True,
  903. conditional_name="servergenerated",
  904. )
  905. self._assert_fk_diff(
  906. diffs[1],
  907. "add_fk",
  908. "user",
  909. ["tid"],
  910. "some_table",
  911. ["id"],
  912. initially=None,
  913. )
  914. @config.requirements.fk_deferrable
  915. @config.requirements.fk_initially
  916. def test_add_initially_immediate_plus_deferrable(self):
  917. diffs = self._fk_opts_fixture(
  918. {}, {"initially": "immediate", "deferrable": True}
  919. )
  920. self._assert_fk_diff(
  921. diffs[0],
  922. "remove_fk",
  923. "user",
  924. ["tid"],
  925. "some_table",
  926. ["id"],
  927. initially=None,
  928. conditional_name="servergenerated",
  929. )
  930. self._assert_fk_diff(
  931. diffs[1],
  932. "add_fk",
  933. "user",
  934. ["tid"],
  935. "some_table",
  936. ["id"],
  937. initially="immediate",
  938. deferrable=True,
  939. )
  940. @config.requirements.fk_deferrable
  941. @config.requirements.fk_initially
  942. def test_remove_initially_immediate_plus_deferrable(self):
  943. diffs = self._fk_opts_fixture(
  944. {"initially": "immediate", "deferrable": True}, {}
  945. )
  946. self._assert_fk_diff(
  947. diffs[0],
  948. "remove_fk",
  949. "user",
  950. ["tid"],
  951. "some_table",
  952. ["id"],
  953. initially=None, # immediate is the default
  954. deferrable=True,
  955. conditional_name="servergenerated",
  956. )
  957. self._assert_fk_diff(
  958. diffs[1],
  959. "add_fk",
  960. "user",
  961. ["tid"],
  962. "some_table",
  963. ["id"],
  964. initially=None,
  965. deferrable=None,
  966. )
  967. @config.requirements.fk_initially
  968. @config.requirements.fk_deferrable
  969. def test_add_initially_deferrable_nochange_one(self):
  970. diffs = self._fk_opts_fixture(
  971. {"deferrable": True, "initially": "immediate"},
  972. {"deferrable": True, "initially": "immediate"},
  973. )
  974. eq_(diffs, [])
  975. @config.requirements.fk_initially
  976. @config.requirements.fk_deferrable
  977. def test_add_initially_deferrable_nochange_two(self):
  978. diffs = self._fk_opts_fixture(
  979. {"deferrable": True, "initially": "deferred"},
  980. {"deferrable": True, "initially": "deferred"},
  981. )
  982. eq_(diffs, [])
  983. @config.requirements.fk_initially
  984. @config.requirements.fk_deferrable
  985. def test_add_initially_deferrable_nochange_three(self):
  986. diffs = self._fk_opts_fixture(
  987. {"deferrable": None, "initially": "deferred"},
  988. {"deferrable": None, "initially": "deferred"},
  989. )
  990. eq_(diffs, [])
  991. @config.requirements.fk_deferrable
  992. def test_add_deferrable(self):
  993. diffs = self._fk_opts_fixture({}, {"deferrable": True})
  994. self._assert_fk_diff(
  995. diffs[0],
  996. "remove_fk",
  997. "user",
  998. ["tid"],
  999. "some_table",
  1000. ["id"],
  1001. deferrable=None,
  1002. conditional_name="servergenerated",
  1003. )
  1004. self._assert_fk_diff(
  1005. diffs[1],
  1006. "add_fk",
  1007. "user",
  1008. ["tid"],
  1009. "some_table",
  1010. ["id"],
  1011. deferrable=True,
  1012. )
  1013. @config.requirements.fk_deferrable_is_reflected
  1014. def test_remove_deferrable(self):
  1015. diffs = self._fk_opts_fixture({"deferrable": True}, {})
  1016. self._assert_fk_diff(
  1017. diffs[0],
  1018. "remove_fk",
  1019. "user",
  1020. ["tid"],
  1021. "some_table",
  1022. ["id"],
  1023. deferrable=True,
  1024. conditional_name="servergenerated",
  1025. )
  1026. self._assert_fk_diff(
  1027. diffs[1],
  1028. "add_fk",
  1029. "user",
  1030. ["tid"],
  1031. "some_table",
  1032. ["id"],
  1033. deferrable=None,
  1034. )