PageRenderTime 38ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/test/sql/test_constraints.py

https://bitbucket.org/sqlalchemy/sqlalchemy/
Python | 487 lines | 401 code | 76 blank | 10 comment | 3 complexity | 41aef1a4b14632a30cae490d5df539fc MD5 | raw file
  1. from test.lib.testing import assert_raises, assert_raises_message
  2. from sqlalchemy import *
  3. from sqlalchemy import exc, schema
  4. from test.lib import *
  5. from test.lib import config, engines
  6. from sqlalchemy.engine import ddl
  7. from test.lib.testing import eq_
  8. from test.lib.assertsql import AllOf, RegexSQL, ExactSQL, CompiledSQL
  9. from sqlalchemy.dialects.postgresql import base as postgresql
  10. class ConstraintTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiledSQL):
  11. def setup(self):
  12. global metadata
  13. metadata = MetaData(testing.db)
  14. def teardown(self):
  15. metadata.drop_all()
  16. def test_constraint(self):
  17. employees = Table('employees', metadata,
  18. Column('id', Integer),
  19. Column('soc', String(40)),
  20. Column('name', String(30)),
  21. PrimaryKeyConstraint('id', 'soc')
  22. )
  23. elements = Table('elements', metadata,
  24. Column('id', Integer),
  25. Column('stuff', String(30)),
  26. Column('emp_id', Integer),
  27. Column('emp_soc', String(40)),
  28. PrimaryKeyConstraint('id', name='elements_primkey'),
  29. ForeignKeyConstraint(['emp_id', 'emp_soc'], ['employees.id', 'employees.soc'])
  30. )
  31. metadata.create_all()
  32. def test_double_fk_usage_raises(self):
  33. f = ForeignKey('b.id')
  34. Column('x', Integer, f)
  35. assert_raises(exc.InvalidRequestError, Column, "y", Integer, f)
  36. def test_circular_constraint(self):
  37. a = Table("a", metadata,
  38. Column('id', Integer, primary_key=True),
  39. Column('bid', Integer),
  40. ForeignKeyConstraint(["bid"], ["b.id"], name="afk")
  41. )
  42. b = Table("b", metadata,
  43. Column('id', Integer, primary_key=True),
  44. Column("aid", Integer),
  45. ForeignKeyConstraint(["aid"], ["a.id"], use_alter=True, name="bfk")
  46. )
  47. metadata.create_all()
  48. def test_circular_constraint_2(self):
  49. a = Table("a", metadata,
  50. Column('id', Integer, primary_key=True),
  51. Column('bid', Integer, ForeignKey("b.id")),
  52. )
  53. b = Table("b", metadata,
  54. Column('id', Integer, primary_key=True),
  55. Column("aid", Integer, ForeignKey("a.id", use_alter=True, name="bfk")),
  56. )
  57. metadata.create_all()
  58. @testing.fails_on('mysql', 'FIXME: unknown')
  59. def test_check_constraint(self):
  60. foo = Table('foo', metadata,
  61. Column('id', Integer, primary_key=True),
  62. Column('x', Integer),
  63. Column('y', Integer),
  64. CheckConstraint('x>y'))
  65. bar = Table('bar', metadata,
  66. Column('id', Integer, primary_key=True),
  67. Column('x', Integer, CheckConstraint('x>7')),
  68. Column('z', Integer)
  69. )
  70. metadata.create_all()
  71. foo.insert().execute(id=1,x=9,y=5)
  72. assert_raises(exc.DBAPIError, foo.insert().execute, id=2,x=5,y=9)
  73. bar.insert().execute(id=1,x=10)
  74. assert_raises(exc.DBAPIError, bar.insert().execute, id=2,x=5)
  75. def test_unique_constraint(self):
  76. foo = Table('foo', metadata,
  77. Column('id', Integer, primary_key=True),
  78. Column('value', String(30), unique=True))
  79. bar = Table('bar', metadata,
  80. Column('id', Integer, primary_key=True),
  81. Column('value', String(30)),
  82. Column('value2', String(30)),
  83. UniqueConstraint('value', 'value2', name='uix1')
  84. )
  85. metadata.create_all()
  86. foo.insert().execute(id=1, value='value1')
  87. foo.insert().execute(id=2, value='value2')
  88. bar.insert().execute(id=1, value='a', value2='a')
  89. bar.insert().execute(id=2, value='a', value2='b')
  90. assert_raises(exc.DBAPIError, foo.insert().execute, id=3, value='value1')
  91. assert_raises(exc.DBAPIError, bar.insert().execute, id=3, value='a', value2='b')
  92. def test_index_create(self):
  93. employees = Table('employees', metadata,
  94. Column('id', Integer, primary_key=True),
  95. Column('first_name', String(30)),
  96. Column('last_name', String(30)),
  97. Column('email_address', String(30)))
  98. employees.create()
  99. i = Index('employee_name_index',
  100. employees.c.last_name, employees.c.first_name)
  101. i.create()
  102. assert i in employees.indexes
  103. i2 = Index('employee_email_index',
  104. employees.c.email_address, unique=True)
  105. i2.create()
  106. assert i2 in employees.indexes
  107. def test_index_create_camelcase(self):
  108. """test that mixed-case index identifiers are legal"""
  109. employees = Table('companyEmployees', metadata,
  110. Column('id', Integer, primary_key=True),
  111. Column('firstName', String(30)),
  112. Column('lastName', String(30)),
  113. Column('emailAddress', String(30)))
  114. employees.create()
  115. i = Index('employeeNameIndex',
  116. employees.c.lastName, employees.c.firstName)
  117. i.create()
  118. i = Index('employeeEmailIndex',
  119. employees.c.emailAddress, unique=True)
  120. i.create()
  121. # Check that the table is useable. This is mostly for pg,
  122. # which can be somewhat sticky with mixed-case identifiers
  123. employees.insert().execute(firstName='Joe', lastName='Smith', id=0)
  124. ss = employees.select().execute().fetchall()
  125. assert ss[0].firstName == 'Joe'
  126. assert ss[0].lastName == 'Smith'
  127. def test_index_create_inline(self):
  128. """Test indexes defined with tables"""
  129. events = Table('events', metadata,
  130. Column('id', Integer, primary_key=True),
  131. Column('name', String(30), index=True, unique=True),
  132. Column('location', String(30), index=True),
  133. Column('sport', String(30)),
  134. Column('announcer', String(30)),
  135. Column('winner', String(30)))
  136. Index('sport_announcer', events.c.sport, events.c.announcer, unique=True)
  137. Index('idx_winners', events.c.winner)
  138. eq_(
  139. set([ ix.name for ix in events.indexes ]),
  140. set(['ix_events_name', 'ix_events_location', 'sport_announcer', 'idx_winners'])
  141. )
  142. self.assert_sql_execution(
  143. testing.db,
  144. lambda: events.create(testing.db),
  145. RegexSQL("^CREATE TABLE events"),
  146. AllOf(
  147. ExactSQL('CREATE UNIQUE INDEX ix_events_name ON events (name)'),
  148. ExactSQL('CREATE INDEX ix_events_location ON events (location)'),
  149. ExactSQL('CREATE UNIQUE INDEX sport_announcer ON events (sport, announcer)'),
  150. ExactSQL('CREATE INDEX idx_winners ON events (winner)')
  151. )
  152. )
  153. # verify that the table is functional
  154. events.insert().execute(id=1, name='hockey finals', location='rink',
  155. sport='hockey', announcer='some canadian',
  156. winner='sweden')
  157. ss = events.select().execute().fetchall()
  158. def test_too_long_idx_name(self):
  159. dialect = testing.db.dialect.__class__()
  160. for max_ident, max_index in [(22, None), (256, 22)]:
  161. dialect.max_identifier_length = max_ident
  162. dialect.max_index_name_length = max_index
  163. for tname, cname, exp in [
  164. ('sometable', 'this_name_is_too_long', 'ix_sometable_t_09aa'),
  165. ('sometable', 'this_name_alsois_long', 'ix_sometable_t_3cf1'),
  166. ]:
  167. t1 = Table(tname, MetaData(),
  168. Column(cname, Integer, index=True),
  169. )
  170. ix1 = list(t1.indexes)[0]
  171. self.assert_compile(
  172. schema.CreateIndex(ix1),
  173. "CREATE INDEX %s "
  174. "ON %s (%s)" % (exp, tname, cname),
  175. dialect=dialect
  176. )
  177. dialect.max_identifier_length = 22
  178. dialect.max_index_name_length = None
  179. t1 = Table('t', MetaData(), Column('c', Integer))
  180. assert_raises(
  181. exc.IdentifierError,
  182. schema.CreateIndex(Index(
  183. "this_other_name_is_too_long_for_what_were_doing",
  184. t1.c.c)).compile,
  185. dialect=dialect
  186. )
  187. def test_index_declartion_inline(self):
  188. t1 = Table('t1', metadata,
  189. Column('x', Integer),
  190. Column('y', Integer),
  191. Index('foo', 'x', 'y')
  192. )
  193. self.assert_compile(
  194. schema.CreateIndex(list(t1.indexes)[0]),
  195. "CREATE INDEX foo ON t1 (x, y)"
  196. )
  197. def test_index_asserts_cols_standalone(self):
  198. t1 = Table('t1', metadata,
  199. Column('x', Integer)
  200. )
  201. t2 = Table('t2', metadata,
  202. Column('y', Integer)
  203. )
  204. assert_raises_message(
  205. exc.ArgumentError,
  206. "Column 't2.y' is not part of table 't1'.",
  207. Index,
  208. "bar", t1.c.x, t2.c.y
  209. )
  210. def test_index_asserts_cols_inline(self):
  211. t1 = Table('t1', metadata,
  212. Column('x', Integer)
  213. )
  214. assert_raises_message(
  215. exc.ArgumentError,
  216. "Index 'bar' is against table 't1', and "
  217. "cannot be associated with table 't2'.",
  218. Table, 't2', metadata,
  219. Column('y', Integer),
  220. Index('bar', t1.c.x)
  221. )
  222. class ConstraintCompilationTest(fixtures.TestBase, AssertsCompiledSQL):
  223. __dialect__ = 'default'
  224. def _test_deferrable(self, constraint_factory):
  225. t = Table('tbl', MetaData(),
  226. Column('a', Integer),
  227. Column('b', Integer),
  228. constraint_factory(deferrable=True))
  229. sql = str(schema.CreateTable(t).compile(bind=testing.db))
  230. assert 'DEFERRABLE' in sql, sql
  231. assert 'NOT DEFERRABLE' not in sql, sql
  232. t = Table('tbl', MetaData(),
  233. Column('a', Integer),
  234. Column('b', Integer),
  235. constraint_factory(deferrable=False))
  236. sql = str(schema.CreateTable(t).compile(bind=testing.db))
  237. assert 'NOT DEFERRABLE' in sql
  238. t = Table('tbl', MetaData(),
  239. Column('a', Integer),
  240. Column('b', Integer),
  241. constraint_factory(deferrable=True, initially='IMMEDIATE'))
  242. sql = str(schema.CreateTable(t).compile(bind=testing.db))
  243. assert 'NOT DEFERRABLE' not in sql
  244. assert 'INITIALLY IMMEDIATE' in sql
  245. t = Table('tbl', MetaData(),
  246. Column('a', Integer),
  247. Column('b', Integer),
  248. constraint_factory(deferrable=True, initially='DEFERRED'))
  249. sql = str(schema.CreateTable(t).compile(bind=testing.db))
  250. assert 'NOT DEFERRABLE' not in sql
  251. assert 'INITIALLY DEFERRED' in sql
  252. def test_column_level_ck_name(self):
  253. t = Table('tbl', MetaData(),
  254. Column('a', Integer, CheckConstraint("a > 5", name="ck_a_greater_five"))
  255. )
  256. self.assert_compile(
  257. schema.CreateTable(t),
  258. "CREATE TABLE tbl (a INTEGER CONSTRAINT "
  259. "ck_a_greater_five CHECK (a > 5))"
  260. )
  261. def test_deferrable_pk(self):
  262. factory = lambda **kw: PrimaryKeyConstraint('a', **kw)
  263. self._test_deferrable(factory)
  264. def test_deferrable_table_fk(self):
  265. factory = lambda **kw: ForeignKeyConstraint(['b'], ['tbl.a'], **kw)
  266. self._test_deferrable(factory)
  267. def test_deferrable_column_fk(self):
  268. t = Table('tbl', MetaData(),
  269. Column('a', Integer),
  270. Column('b', Integer,
  271. ForeignKey('tbl.a', deferrable=True,
  272. initially='DEFERRED')))
  273. self.assert_compile(
  274. schema.CreateTable(t),
  275. "CREATE TABLE tbl (a INTEGER, b INTEGER, "
  276. "FOREIGN KEY(b) REFERENCES tbl "
  277. "(a) DEFERRABLE INITIALLY DEFERRED)",
  278. )
  279. def test_deferrable_unique(self):
  280. factory = lambda **kw: UniqueConstraint('b', **kw)
  281. self._test_deferrable(factory)
  282. def test_deferrable_table_check(self):
  283. factory = lambda **kw: CheckConstraint('a < b', **kw)
  284. self._test_deferrable(factory)
  285. def test_multiple(self):
  286. m = MetaData()
  287. foo = Table("foo", m,
  288. Column('id', Integer, primary_key=True),
  289. Column('bar', Integer, primary_key=True)
  290. )
  291. tb = Table("some_table", m,
  292. Column('id', Integer, primary_key=True),
  293. Column('foo_id', Integer, ForeignKey('foo.id')),
  294. Column('foo_bar', Integer, ForeignKey('foo.bar')),
  295. )
  296. self.assert_compile(
  297. schema.CreateTable(tb),
  298. "CREATE TABLE some_table ("
  299. "id INTEGER NOT NULL, "
  300. "foo_id INTEGER, "
  301. "foo_bar INTEGER, "
  302. "PRIMARY KEY (id), "
  303. "FOREIGN KEY(foo_id) REFERENCES foo (id), "
  304. "FOREIGN KEY(foo_bar) REFERENCES foo (bar))"
  305. )
  306. def test_deferrable_column_check(self):
  307. t = Table('tbl', MetaData(),
  308. Column('a', Integer),
  309. Column('b', Integer,
  310. CheckConstraint('a < b',
  311. deferrable=True,
  312. initially='DEFERRED')))
  313. self.assert_compile(
  314. schema.CreateTable(t),
  315. "CREATE TABLE tbl (a INTEGER, b INTEGER CHECK (a < b) DEFERRABLE INITIALLY DEFERRED)"
  316. )
  317. def test_use_alter(self):
  318. m = MetaData()
  319. t = Table('t', m,
  320. Column('a', Integer),
  321. )
  322. t2 = Table('t2', m,
  323. Column('a', Integer, ForeignKey('t.a', use_alter=True, name='fk_ta')),
  324. Column('b', Integer, ForeignKey('t.a', name='fk_tb')), # to ensure create ordering ...
  325. )
  326. e = engines.mock_engine(dialect_name='postgresql')
  327. m.create_all(e)
  328. m.drop_all(e)
  329. e.assert_sql([
  330. 'CREATE TABLE t (a INTEGER)',
  331. 'CREATE TABLE t2 (a INTEGER, b INTEGER, CONSTRAINT fk_tb FOREIGN KEY(b) REFERENCES t (a))',
  332. 'ALTER TABLE t2 ADD CONSTRAINT fk_ta FOREIGN KEY(a) REFERENCES t (a)',
  333. 'ALTER TABLE t2 DROP CONSTRAINT fk_ta',
  334. 'DROP TABLE t2',
  335. 'DROP TABLE t'
  336. ])
  337. def test_add_drop_constraint(self):
  338. m = MetaData()
  339. t = Table('tbl', m,
  340. Column('a', Integer),
  341. Column('b', Integer)
  342. )
  343. t2 = Table('t2', m,
  344. Column('a', Integer),
  345. Column('b', Integer)
  346. )
  347. constraint = CheckConstraint('a < b',name="my_test_constraint",
  348. deferrable=True,initially='DEFERRED', table=t)
  349. # before we create an AddConstraint,
  350. # the CONSTRAINT comes out inline
  351. self.assert_compile(
  352. schema.CreateTable(t),
  353. "CREATE TABLE tbl ("
  354. "a INTEGER, "
  355. "b INTEGER, "
  356. "CONSTRAINT my_test_constraint CHECK (a < b) DEFERRABLE INITIALLY DEFERRED"
  357. ")"
  358. )
  359. self.assert_compile(
  360. schema.AddConstraint(constraint),
  361. "ALTER TABLE tbl ADD CONSTRAINT my_test_constraint "
  362. "CHECK (a < b) DEFERRABLE INITIALLY DEFERRED"
  363. )
  364. # once we make an AddConstraint,
  365. # inline compilation of the CONSTRAINT
  366. # is disabled
  367. self.assert_compile(
  368. schema.CreateTable(t),
  369. "CREATE TABLE tbl ("
  370. "a INTEGER, "
  371. "b INTEGER"
  372. ")"
  373. )
  374. self.assert_compile(
  375. schema.DropConstraint(constraint),
  376. "ALTER TABLE tbl DROP CONSTRAINT my_test_constraint"
  377. )
  378. self.assert_compile(
  379. schema.DropConstraint(constraint, cascade=True),
  380. "ALTER TABLE tbl DROP CONSTRAINT my_test_constraint CASCADE"
  381. )
  382. constraint = ForeignKeyConstraint(["b"], ["t2.a"])
  383. t.append_constraint(constraint)
  384. self.assert_compile(
  385. schema.AddConstraint(constraint),
  386. "ALTER TABLE tbl ADD FOREIGN KEY(b) REFERENCES t2 (a)"
  387. )
  388. constraint = ForeignKeyConstraint([t.c.a], [t2.c.b])
  389. t.append_constraint(constraint)
  390. self.assert_compile(
  391. schema.AddConstraint(constraint),
  392. "ALTER TABLE tbl ADD FOREIGN KEY(a) REFERENCES t2 (b)"
  393. )
  394. constraint = UniqueConstraint("a", "b", name="uq_cst")
  395. t2.append_constraint(constraint)
  396. self.assert_compile(
  397. schema.AddConstraint(constraint),
  398. "ALTER TABLE t2 ADD CONSTRAINT uq_cst UNIQUE (a, b)"
  399. )
  400. constraint = UniqueConstraint(t2.c.a, t2.c.b, name="uq_cs2")
  401. self.assert_compile(
  402. schema.AddConstraint(constraint),
  403. "ALTER TABLE t2 ADD CONSTRAINT uq_cs2 UNIQUE (a, b)"
  404. )
  405. assert t.c.a.primary_key is False
  406. constraint = PrimaryKeyConstraint(t.c.a)
  407. assert t.c.a.primary_key is True
  408. self.assert_compile(
  409. schema.AddConstraint(constraint),
  410. "ALTER TABLE tbl ADD PRIMARY KEY (a)"
  411. )