PageRenderTime 80ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/SQLAlchemy-0.7.8/test/dialect/test_mysql.py

#
Python | 1668 lines | 1628 code | 25 blank | 15 comment | 12 complexity | 3976419ae171c99a680c2eef9e5df809 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. # coding: utf-8
  2. from test.lib.testing import eq_, assert_raises, assert_raises_message
  3. # Py2K
  4. import sets
  5. # end Py2K
  6. from sqlalchemy import *
  7. from sqlalchemy import sql, exc, schema, types as sqltypes, event
  8. from sqlalchemy.dialects.mysql import base as mysql
  9. from sqlalchemy.engine.url import make_url
  10. from test.lib.testing import eq_
  11. from test.lib import *
  12. from test.lib.engines import utf8_engine
  13. import datetime
  14. class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
  15. __dialect__ = mysql.dialect()
  16. def test_reserved_words(self):
  17. table = Table("mysql_table", MetaData(),
  18. Column("col1", Integer),
  19. Column("master_ssl_verify_server_cert", Integer))
  20. x = select([table.c.col1, table.c.master_ssl_verify_server_cert])
  21. self.assert_compile(x,
  22. '''SELECT mysql_table.col1, mysql_table.`master_ssl_verify_server_cert` FROM mysql_table''')
  23. def test_create_index_simple(self):
  24. m = MetaData()
  25. tbl = Table('testtbl', m, Column('data', String(255)))
  26. idx = Index('test_idx1', tbl.c.data)
  27. self.assert_compile(schema.CreateIndex(idx),
  28. 'CREATE INDEX test_idx1 ON testtbl (data)',
  29. dialect=mysql.dialect())
  30. def test_create_index_with_length(self):
  31. m = MetaData()
  32. tbl = Table('testtbl', m, Column('data', String(255)))
  33. idx1 = Index('test_idx1', tbl.c.data, mysql_length=10)
  34. idx2 = Index('test_idx2', tbl.c.data, mysql_length=5)
  35. self.assert_compile(schema.CreateIndex(idx1),
  36. 'CREATE INDEX test_idx1 ON testtbl (data(10))',
  37. dialect=mysql.dialect())
  38. self.assert_compile(schema.CreateIndex(idx2),
  39. 'CREATE INDEX test_idx2 ON testtbl (data(5))',
  40. dialect=mysql.dialect())
  41. def test_create_index_with_using(self):
  42. m = MetaData()
  43. tbl = Table('testtbl', m, Column('data', String(255)))
  44. idx1 = Index('test_idx1', tbl.c.data, mysql_using='btree')
  45. idx2 = Index('test_idx2', tbl.c.data, mysql_using='hash')
  46. self.assert_compile(schema.CreateIndex(idx1),
  47. 'CREATE INDEX test_idx1 ON testtbl (data) USING btree',
  48. dialect=mysql.dialect())
  49. self.assert_compile(schema.CreateIndex(idx2),
  50. 'CREATE INDEX test_idx2 ON testtbl (data) USING hash',
  51. dialect=mysql.dialect())
  52. def test_create_pk_plain(self):
  53. m = MetaData()
  54. tbl = Table('testtbl', m, Column('data', String(255)),
  55. PrimaryKeyConstraint('data'))
  56. self.assert_compile(schema.CreateTable(tbl),
  57. "CREATE TABLE testtbl (data VARCHAR(255), PRIMARY KEY (data))",
  58. dialect=mysql.dialect())
  59. def test_create_pk_with_using(self):
  60. m = MetaData()
  61. tbl = Table('testtbl', m, Column('data', String(255)),
  62. PrimaryKeyConstraint('data', mysql_using='btree'))
  63. self.assert_compile(schema.CreateTable(tbl),
  64. "CREATE TABLE testtbl (data VARCHAR(255), "
  65. "PRIMARY KEY (data) USING btree)",
  66. dialect=mysql.dialect())
  67. class DialectTest(fixtures.TestBase):
  68. __only_on__ = 'mysql'
  69. @testing.only_on(['mysql+mysqldb', 'mysql+oursql'],
  70. 'requires particular SSL arguments')
  71. def test_ssl_arguments(self):
  72. dialect = testing.db.dialect
  73. kwarg = dialect.create_connect_args(
  74. make_url("mysql://scott:tiger@localhost:3306/test"
  75. "?ssl_ca=/ca.pem&ssl_cert=/cert.pem&ssl_key=/key.pem")
  76. )[1]
  77. # args that differ among mysqldb and oursql
  78. for k in ('use_unicode', 'found_rows', 'client_flag'):
  79. kwarg.pop(k, None)
  80. eq_(
  81. kwarg,
  82. {
  83. 'passwd': 'tiger', 'db': 'test',
  84. 'ssl': {'ca': '/ca.pem', 'cert': '/cert.pem',
  85. 'key': '/key.pem'},
  86. 'host': 'localhost', 'user': 'scott',
  87. 'port': 3306
  88. }
  89. )
  90. class TypesTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiledSQL):
  91. "Test MySQL column types"
  92. __only_on__ = 'mysql'
  93. __dialect__ = mysql.dialect()
  94. @testing.uses_deprecated('Manually quoting ENUM value literals')
  95. def test_basic(self):
  96. meta1 = MetaData(testing.db)
  97. table = Table(
  98. 'mysql_types', meta1,
  99. Column('id', Integer, primary_key=True),
  100. Column('num1', mysql.MSInteger(unsigned=True)),
  101. Column('text1', mysql.MSLongText),
  102. Column('text2', mysql.MSLongText()),
  103. Column('num2', mysql.MSBigInteger),
  104. Column('num3', mysql.MSBigInteger()),
  105. Column('num4', mysql.MSDouble),
  106. Column('num5', mysql.MSDouble()),
  107. Column('num6', mysql.MSMediumInteger),
  108. Column('enum1', mysql.ENUM("'black'", "'white'")),
  109. Column('enum2', mysql.ENUM("dog", "cat")),
  110. )
  111. try:
  112. table.drop(checkfirst=True)
  113. table.create()
  114. meta2 = MetaData(testing.db)
  115. t2 = Table('mysql_types', meta2, autoload=True)
  116. assert isinstance(t2.c.num1.type, mysql.MSInteger)
  117. assert t2.c.num1.type.unsigned
  118. assert isinstance(t2.c.text1.type, mysql.MSLongText)
  119. assert isinstance(t2.c.text2.type, mysql.MSLongText)
  120. assert isinstance(t2.c.num2.type, mysql.MSBigInteger)
  121. assert isinstance(t2.c.num3.type, mysql.MSBigInteger)
  122. assert isinstance(t2.c.num4.type, mysql.MSDouble)
  123. assert isinstance(t2.c.num5.type, mysql.MSDouble)
  124. assert isinstance(t2.c.num6.type, mysql.MSMediumInteger)
  125. assert isinstance(t2.c.enum1.type, mysql.ENUM)
  126. assert isinstance(t2.c.enum2.type, mysql.ENUM)
  127. t2.drop()
  128. t2.create()
  129. finally:
  130. meta1.drop_all()
  131. def test_numeric(self):
  132. "Exercise type specification and options for numeric types."
  133. columns = [
  134. # column type, args, kwargs, expected ddl
  135. # e.g. Column(Integer(10, unsigned=True)) ==
  136. # 'INTEGER(10) UNSIGNED'
  137. (mysql.MSNumeric, [], {},
  138. 'NUMERIC'),
  139. (mysql.MSNumeric, [None], {},
  140. 'NUMERIC'),
  141. (mysql.MSNumeric, [12], {},
  142. 'NUMERIC(12)'),
  143. (mysql.MSNumeric, [12, 4], {'unsigned':True},
  144. 'NUMERIC(12, 4) UNSIGNED'),
  145. (mysql.MSNumeric, [12, 4], {'zerofill':True},
  146. 'NUMERIC(12, 4) ZEROFILL'),
  147. (mysql.MSNumeric, [12, 4], {'zerofill':True, 'unsigned':True},
  148. 'NUMERIC(12, 4) UNSIGNED ZEROFILL'),
  149. (mysql.MSDecimal, [], {},
  150. 'DECIMAL'),
  151. (mysql.MSDecimal, [None], {},
  152. 'DECIMAL'),
  153. (mysql.MSDecimal, [12], {},
  154. 'DECIMAL(12)'),
  155. (mysql.MSDecimal, [12, None], {},
  156. 'DECIMAL(12)'),
  157. (mysql.MSDecimal, [12, 4], {'unsigned':True},
  158. 'DECIMAL(12, 4) UNSIGNED'),
  159. (mysql.MSDecimal, [12, 4], {'zerofill':True},
  160. 'DECIMAL(12, 4) ZEROFILL'),
  161. (mysql.MSDecimal, [12, 4], {'zerofill':True, 'unsigned':True},
  162. 'DECIMAL(12, 4) UNSIGNED ZEROFILL'),
  163. (mysql.MSDouble, [None, None], {},
  164. 'DOUBLE'),
  165. (mysql.MSDouble, [12, 4], {'unsigned':True},
  166. 'DOUBLE(12, 4) UNSIGNED'),
  167. (mysql.MSDouble, [12, 4], {'zerofill':True},
  168. 'DOUBLE(12, 4) ZEROFILL'),
  169. (mysql.MSDouble, [12, 4], {'zerofill':True, 'unsigned':True},
  170. 'DOUBLE(12, 4) UNSIGNED ZEROFILL'),
  171. (mysql.MSReal, [None, None], {},
  172. 'REAL'),
  173. (mysql.MSReal, [12, 4], {'unsigned':True},
  174. 'REAL(12, 4) UNSIGNED'),
  175. (mysql.MSReal, [12, 4], {'zerofill':True},
  176. 'REAL(12, 4) ZEROFILL'),
  177. (mysql.MSReal, [12, 4], {'zerofill':True, 'unsigned':True},
  178. 'REAL(12, 4) UNSIGNED ZEROFILL'),
  179. (mysql.MSFloat, [], {},
  180. 'FLOAT'),
  181. (mysql.MSFloat, [None], {},
  182. 'FLOAT'),
  183. (mysql.MSFloat, [12], {},
  184. 'FLOAT(12)'),
  185. (mysql.MSFloat, [12, 4], {},
  186. 'FLOAT(12, 4)'),
  187. (mysql.MSFloat, [12, 4], {'unsigned':True},
  188. 'FLOAT(12, 4) UNSIGNED'),
  189. (mysql.MSFloat, [12, 4], {'zerofill':True},
  190. 'FLOAT(12, 4) ZEROFILL'),
  191. (mysql.MSFloat, [12, 4], {'zerofill':True, 'unsigned':True},
  192. 'FLOAT(12, 4) UNSIGNED ZEROFILL'),
  193. (mysql.MSInteger, [], {},
  194. 'INTEGER'),
  195. (mysql.MSInteger, [4], {},
  196. 'INTEGER(4)'),
  197. (mysql.MSInteger, [4], {'unsigned':True},
  198. 'INTEGER(4) UNSIGNED'),
  199. (mysql.MSInteger, [4], {'zerofill':True},
  200. 'INTEGER(4) ZEROFILL'),
  201. (mysql.MSInteger, [4], {'zerofill':True, 'unsigned':True},
  202. 'INTEGER(4) UNSIGNED ZEROFILL'),
  203. (mysql.MSBigInteger, [], {},
  204. 'BIGINT'),
  205. (mysql.MSBigInteger, [4], {},
  206. 'BIGINT(4)'),
  207. (mysql.MSBigInteger, [4], {'unsigned':True},
  208. 'BIGINT(4) UNSIGNED'),
  209. (mysql.MSBigInteger, [4], {'zerofill':True},
  210. 'BIGINT(4) ZEROFILL'),
  211. (mysql.MSBigInteger, [4], {'zerofill':True, 'unsigned':True},
  212. 'BIGINT(4) UNSIGNED ZEROFILL'),
  213. (mysql.MSMediumInteger, [], {},
  214. 'MEDIUMINT'),
  215. (mysql.MSMediumInteger, [4], {},
  216. 'MEDIUMINT(4)'),
  217. (mysql.MSMediumInteger, [4], {'unsigned':True},
  218. 'MEDIUMINT(4) UNSIGNED'),
  219. (mysql.MSMediumInteger, [4], {'zerofill':True},
  220. 'MEDIUMINT(4) ZEROFILL'),
  221. (mysql.MSMediumInteger, [4], {'zerofill':True, 'unsigned':True},
  222. 'MEDIUMINT(4) UNSIGNED ZEROFILL'),
  223. (mysql.MSTinyInteger, [], {},
  224. 'TINYINT'),
  225. (mysql.MSTinyInteger, [1], {},
  226. 'TINYINT(1)'),
  227. (mysql.MSTinyInteger, [1], {'unsigned':True},
  228. 'TINYINT(1) UNSIGNED'),
  229. (mysql.MSTinyInteger, [1], {'zerofill':True},
  230. 'TINYINT(1) ZEROFILL'),
  231. (mysql.MSTinyInteger, [1], {'zerofill':True, 'unsigned':True},
  232. 'TINYINT(1) UNSIGNED ZEROFILL'),
  233. (mysql.MSSmallInteger, [], {},
  234. 'SMALLINT'),
  235. (mysql.MSSmallInteger, [4], {},
  236. 'SMALLINT(4)'),
  237. (mysql.MSSmallInteger, [4], {'unsigned':True},
  238. 'SMALLINT(4) UNSIGNED'),
  239. (mysql.MSSmallInteger, [4], {'zerofill':True},
  240. 'SMALLINT(4) ZEROFILL'),
  241. (mysql.MSSmallInteger, [4], {'zerofill':True, 'unsigned':True},
  242. 'SMALLINT(4) UNSIGNED ZEROFILL'),
  243. ]
  244. table_args = ['test_mysql_numeric', MetaData(testing.db)]
  245. for index, spec in enumerate(columns):
  246. type_, args, kw, res = spec
  247. table_args.append(Column('c%s' % index, type_(*args, **kw)))
  248. numeric_table = Table(*table_args)
  249. gen = testing.db.dialect.ddl_compiler(testing.db.dialect, None)
  250. for col in numeric_table.c:
  251. index = int(col.name[1:])
  252. eq_(gen.get_column_specification(col),
  253. "%s %s" % (col.name, columns[index][3]))
  254. self.assert_(repr(col))
  255. try:
  256. numeric_table.create(checkfirst=True)
  257. assert True
  258. except:
  259. raise
  260. numeric_table.drop()
  261. @testing.exclude('mysql', '<', (4, 1, 1), 'no charset support')
  262. def test_charset(self):
  263. """Exercise CHARACTER SET and COLLATE-ish options on string types."""
  264. columns = [
  265. (mysql.MSChar, [1], {},
  266. 'CHAR(1)'),
  267. (mysql.NCHAR, [1], {},
  268. 'NATIONAL CHAR(1)'),
  269. (mysql.MSChar, [1], {'binary':True},
  270. 'CHAR(1) BINARY'),
  271. (mysql.MSChar, [1], {'ascii':True},
  272. 'CHAR(1) ASCII'),
  273. (mysql.MSChar, [1], {'unicode':True},
  274. 'CHAR(1) UNICODE'),
  275. (mysql.MSChar, [1], {'ascii':True, 'binary':True},
  276. 'CHAR(1) ASCII BINARY'),
  277. (mysql.MSChar, [1], {'unicode':True, 'binary':True},
  278. 'CHAR(1) UNICODE BINARY'),
  279. (mysql.MSChar, [1], {'charset':'utf8'},
  280. 'CHAR(1) CHARACTER SET utf8'),
  281. (mysql.MSChar, [1], {'charset':'utf8', 'binary':True},
  282. 'CHAR(1) CHARACTER SET utf8 BINARY'),
  283. (mysql.MSChar, [1], {'charset':'utf8', 'unicode':True},
  284. 'CHAR(1) CHARACTER SET utf8'),
  285. (mysql.MSChar, [1], {'charset':'utf8', 'ascii':True},
  286. 'CHAR(1) CHARACTER SET utf8'),
  287. (mysql.MSChar, [1], {'collation': 'utf8_bin'},
  288. 'CHAR(1) COLLATE utf8_bin'),
  289. (mysql.MSChar, [1], {'charset': 'utf8', 'collation': 'utf8_bin'},
  290. 'CHAR(1) CHARACTER SET utf8 COLLATE utf8_bin'),
  291. (mysql.MSChar, [1], {'charset': 'utf8', 'binary': True},
  292. 'CHAR(1) CHARACTER SET utf8 BINARY'),
  293. (mysql.MSChar, [1], {'charset': 'utf8', 'collation': 'utf8_bin',
  294. 'binary': True},
  295. 'CHAR(1) CHARACTER SET utf8 COLLATE utf8_bin'),
  296. (mysql.MSChar, [1], {'national':True},
  297. 'NATIONAL CHAR(1)'),
  298. (mysql.MSChar, [1], {'national':True, 'charset':'utf8'},
  299. 'NATIONAL CHAR(1)'),
  300. (mysql.MSChar, [1], {'national':True, 'charset':'utf8',
  301. 'binary':True},
  302. 'NATIONAL CHAR(1) BINARY'),
  303. (mysql.MSChar, [1], {'national':True, 'binary':True,
  304. 'unicode':True},
  305. 'NATIONAL CHAR(1) BINARY'),
  306. (mysql.MSChar, [1], {'national':True, 'collation':'utf8_bin'},
  307. 'NATIONAL CHAR(1) COLLATE utf8_bin'),
  308. (mysql.MSString, [1], {'charset':'utf8', 'collation':'utf8_bin'},
  309. 'VARCHAR(1) CHARACTER SET utf8 COLLATE utf8_bin'),
  310. (mysql.MSString, [1], {'national':True, 'collation':'utf8_bin'},
  311. 'NATIONAL VARCHAR(1) COLLATE utf8_bin'),
  312. (mysql.MSTinyText, [], {'charset':'utf8', 'collation':'utf8_bin'},
  313. 'TINYTEXT CHARACTER SET utf8 COLLATE utf8_bin'),
  314. (mysql.MSMediumText, [], {'charset':'utf8', 'binary':True},
  315. 'MEDIUMTEXT CHARACTER SET utf8 BINARY'),
  316. (mysql.MSLongText, [], {'ascii':True},
  317. 'LONGTEXT ASCII'),
  318. (mysql.ENUM, ["foo", "bar"], {'unicode':True},
  319. '''ENUM('foo','bar') UNICODE''')
  320. ]
  321. table_args = ['test_mysql_charset', MetaData(testing.db)]
  322. for index, spec in enumerate(columns):
  323. type_, args, kw, res = spec
  324. table_args.append(Column('c%s' % index, type_(*args, **kw)))
  325. charset_table = Table(*table_args)
  326. gen = testing.db.dialect.ddl_compiler(testing.db.dialect, None)
  327. for col in charset_table.c:
  328. index = int(col.name[1:])
  329. eq_(gen.get_column_specification(col),
  330. "%s %s" % (col.name, columns[index][3]))
  331. self.assert_(repr(col))
  332. try:
  333. charset_table.create(checkfirst=True)
  334. assert True
  335. except:
  336. raise
  337. charset_table.drop()
  338. @testing.exclude('mysql', '<', (5, 0, 5), 'a 5.0+ feature')
  339. @testing.provide_metadata
  340. def test_charset_collate_table(self):
  341. t = Table('foo', self.metadata,
  342. Column('id', Integer),
  343. mysql_default_charset='utf8',
  344. mysql_collate='utf8_unicode_ci'
  345. )
  346. t.create()
  347. m2 = MetaData(testing.db)
  348. t2 = Table('foo', m2, autoload=True)
  349. eq_(t2.kwargs['mysql_collate'], 'utf8_unicode_ci')
  350. eq_(t2.kwargs['mysql_default charset'], 'utf8')
  351. @testing.exclude('mysql', '<', (5, 0, 5), 'a 5.0+ feature')
  352. @testing.fails_on('mysql+oursql', 'some round trips fail, oursql bug ?')
  353. def test_bit_50(self):
  354. """Exercise BIT types on 5.0+ (not valid for all engine types)"""
  355. meta = MetaData(testing.db)
  356. bit_table = Table('mysql_bits', meta,
  357. Column('b1', mysql.MSBit),
  358. Column('b2', mysql.MSBit()),
  359. Column('b3', mysql.MSBit(), nullable=False),
  360. Column('b4', mysql.MSBit(1)),
  361. Column('b5', mysql.MSBit(8)),
  362. Column('b6', mysql.MSBit(32)),
  363. Column('b7', mysql.MSBit(63)),
  364. Column('b8', mysql.MSBit(64)))
  365. eq_(colspec(bit_table.c.b1), 'b1 BIT')
  366. eq_(colspec(bit_table.c.b2), 'b2 BIT')
  367. eq_(colspec(bit_table.c.b3), 'b3 BIT NOT NULL')
  368. eq_(colspec(bit_table.c.b4), 'b4 BIT(1)')
  369. eq_(colspec(bit_table.c.b5), 'b5 BIT(8)')
  370. eq_(colspec(bit_table.c.b6), 'b6 BIT(32)')
  371. eq_(colspec(bit_table.c.b7), 'b7 BIT(63)')
  372. eq_(colspec(bit_table.c.b8), 'b8 BIT(64)')
  373. for col in bit_table.c:
  374. self.assert_(repr(col))
  375. try:
  376. meta.create_all()
  377. meta2 = MetaData(testing.db)
  378. reflected = Table('mysql_bits', meta2, autoload=True)
  379. for table in bit_table, reflected:
  380. def roundtrip(store, expected=None):
  381. expected = expected or store
  382. table.insert(store).execute()
  383. row = table.select().execute().first()
  384. try:
  385. self.assert_(list(row) == expected)
  386. except:
  387. print "Storing %s" % store
  388. print "Expected %s" % expected
  389. print "Found %s" % list(row)
  390. raise
  391. table.delete().execute().close()
  392. roundtrip([0] * 8)
  393. roundtrip([None, None, 0, None, None, None, None, None])
  394. roundtrip([1] * 8)
  395. roundtrip([sql.text("b'1'")] * 8, [1] * 8)
  396. i = 255
  397. roundtrip([0, 0, 0, 0, i, i, i, i])
  398. i = 2**32 - 1
  399. roundtrip([0, 0, 0, 0, 0, i, i, i])
  400. i = 2**63 - 1
  401. roundtrip([0, 0, 0, 0, 0, 0, i, i])
  402. i = 2**64 - 1
  403. roundtrip([0, 0, 0, 0, 0, 0, 0, i])
  404. finally:
  405. meta.drop_all()
  406. def test_boolean(self):
  407. """Test BOOL/TINYINT(1) compatibility and reflection."""
  408. meta = MetaData(testing.db)
  409. bool_table = Table(
  410. 'mysql_bool',
  411. meta,
  412. Column('b1', BOOLEAN),
  413. Column('b2', Boolean),
  414. Column('b3', mysql.MSTinyInteger(1)),
  415. Column('b4', mysql.MSTinyInteger(1, unsigned=True)),
  416. Column('b5', mysql.MSTinyInteger),
  417. )
  418. eq_(colspec(bool_table.c.b1), 'b1 BOOL')
  419. eq_(colspec(bool_table.c.b2), 'b2 BOOL')
  420. eq_(colspec(bool_table.c.b3), 'b3 TINYINT(1)')
  421. eq_(colspec(bool_table.c.b4), 'b4 TINYINT(1) UNSIGNED')
  422. eq_(colspec(bool_table.c.b5), 'b5 TINYINT')
  423. for col in bool_table.c:
  424. self.assert_(repr(col))
  425. try:
  426. meta.create_all()
  427. table = bool_table
  428. def roundtrip(store, expected=None):
  429. expected = expected or store
  430. table.insert(store).execute()
  431. row = table.select().execute().first()
  432. try:
  433. self.assert_(list(row) == expected)
  434. for i, val in enumerate(expected):
  435. if isinstance(val, bool):
  436. self.assert_(val is row[i])
  437. except:
  438. print 'Storing %s' % store
  439. print 'Expected %s' % expected
  440. print 'Found %s' % list(row)
  441. raise
  442. table.delete().execute().close()
  443. roundtrip([None, None, None, None, None])
  444. roundtrip([True, True, 1, 1, 1])
  445. roundtrip([False, False, 0, 0, 0])
  446. roundtrip([True, True, True, True, True], [True, True, 1,
  447. 1, 1])
  448. roundtrip([False, False, 0, 0, 0], [False, False, 0, 0, 0])
  449. meta2 = MetaData(testing.db)
  450. table = Table('mysql_bool', meta2, autoload=True)
  451. eq_(colspec(table.c.b3), 'b3 TINYINT(1)')
  452. eq_(colspec(table.c.b4), 'b4 TINYINT(1) UNSIGNED')
  453. meta2 = MetaData(testing.db)
  454. table = Table(
  455. 'mysql_bool',
  456. meta2,
  457. Column('b1', BOOLEAN),
  458. Column('b2', Boolean),
  459. Column('b3', BOOLEAN),
  460. Column('b4', BOOLEAN),
  461. autoload=True,
  462. )
  463. eq_(colspec(table.c.b3), 'b3 BOOL')
  464. eq_(colspec(table.c.b4), 'b4 BOOL')
  465. roundtrip([None, None, None, None, None])
  466. roundtrip([True, True, 1, 1, 1], [True, True, True, True,
  467. 1])
  468. roundtrip([False, False, 0, 0, 0], [False, False, False,
  469. False, 0])
  470. roundtrip([True, True, True, True, True], [True, True,
  471. True, True, 1])
  472. roundtrip([False, False, 0, 0, 0], [False, False, False,
  473. False, 0])
  474. finally:
  475. meta.drop_all()
  476. @testing.exclude('mysql', '<', (4, 1, 0), '4.1+ syntax')
  477. def test_timestamp(self):
  478. """Exercise funky TIMESTAMP default syntax."""
  479. meta = MetaData(testing.db)
  480. try:
  481. columns = [
  482. ([TIMESTAMP],
  483. 'TIMESTAMP NULL'),
  484. ([mysql.MSTimeStamp],
  485. 'TIMESTAMP NULL'),
  486. ([mysql.MSTimeStamp,
  487. DefaultClause(sql.text('CURRENT_TIMESTAMP'))],
  488. "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"),
  489. ([mysql.MSTimeStamp,
  490. DefaultClause(sql.text("'1999-09-09 09:09:09'"))],
  491. "TIMESTAMP DEFAULT '1999-09-09 09:09:09'"),
  492. ([mysql.MSTimeStamp,
  493. DefaultClause(sql.text("'1999-09-09 09:09:09' "
  494. "ON UPDATE CURRENT_TIMESTAMP"))],
  495. "TIMESTAMP DEFAULT '1999-09-09 09:09:09' "
  496. "ON UPDATE CURRENT_TIMESTAMP"),
  497. ([mysql.MSTimeStamp,
  498. DefaultClause(sql.text("CURRENT_TIMESTAMP "
  499. "ON UPDATE CURRENT_TIMESTAMP"))],
  500. "TIMESTAMP DEFAULT CURRENT_TIMESTAMP "
  501. "ON UPDATE CURRENT_TIMESTAMP"),
  502. ]
  503. for idx, (spec, expected) in enumerate(columns):
  504. t = Table('mysql_ts%s' % idx, meta,
  505. Column('id', Integer, primary_key=True),
  506. Column('t', *spec))
  507. eq_(colspec(t.c.t), "t %s" % expected)
  508. self.assert_(repr(t.c.t))
  509. t.create()
  510. r = Table('mysql_ts%s' % idx, MetaData(testing.db),
  511. autoload=True)
  512. if len(spec) > 1:
  513. self.assert_(r.c.t is not None)
  514. finally:
  515. meta.drop_all()
  516. def test_timestamp_nullable(self):
  517. meta = MetaData(testing.db)
  518. ts_table = Table('mysql_timestamp', meta,
  519. Column('t1', TIMESTAMP),
  520. Column('t2', TIMESTAMP, nullable=False),
  521. )
  522. meta.create_all()
  523. try:
  524. # there's a slight assumption here that this test can
  525. # complete within the scope of a single second.
  526. # if needed, can break out the eq_() just to check for
  527. # timestamps that are within a few seconds of "now"
  528. # using timedelta.
  529. now = testing.db.execute("select now()").scalar()
  530. # TIMESTAMP without NULL inserts current time when passed
  531. # NULL. when not passed, generates 0000-00-00 quite
  532. # annoyingly.
  533. ts_table.insert().execute({'t1':now, 't2':None})
  534. ts_table.insert().execute({'t1':None, 't2':None})
  535. eq_(
  536. ts_table.select().execute().fetchall(),
  537. [(now, now), (None, now)]
  538. )
  539. finally:
  540. meta.drop_all()
  541. def test_year(self):
  542. """Exercise YEAR."""
  543. meta = MetaData(testing.db)
  544. year_table = Table('mysql_year', meta,
  545. Column('y1', mysql.MSYear),
  546. Column('y2', mysql.MSYear),
  547. Column('y3', mysql.MSYear),
  548. Column('y4', mysql.MSYear(2)),
  549. Column('y5', mysql.MSYear(4)))
  550. for col in year_table.c:
  551. self.assert_(repr(col))
  552. try:
  553. year_table.create()
  554. reflected = Table('mysql_year', MetaData(testing.db),
  555. autoload=True)
  556. for table in year_table, reflected:
  557. table.insert(['1950', '50', None, 50, 1950]).execute()
  558. row = table.select().execute().first()
  559. eq_(list(row), [1950, 2050, None, 50, 1950])
  560. table.delete().execute()
  561. self.assert_(colspec(table.c.y1).startswith('y1 YEAR'))
  562. eq_(colspec(table.c.y4), 'y4 YEAR(2)')
  563. eq_(colspec(table.c.y5), 'y5 YEAR(4)')
  564. finally:
  565. meta.drop_all()
  566. def test_set(self):
  567. """Exercise the SET type."""
  568. meta = MetaData(testing.db)
  569. set_table = Table('mysql_set', meta, Column('s1',
  570. mysql.MSSet("'dq'", "'sq'")), Column('s2',
  571. mysql.MSSet("'a'")), Column('s3',
  572. mysql.MSSet("'5'", "'7'", "'9'")))
  573. eq_(colspec(set_table.c.s1), "s1 SET('dq','sq')")
  574. eq_(colspec(set_table.c.s2), "s2 SET('a')")
  575. eq_(colspec(set_table.c.s3), "s3 SET('5','7','9')")
  576. for col in set_table.c:
  577. self.assert_(repr(col))
  578. try:
  579. set_table.create()
  580. reflected = Table('mysql_set', MetaData(testing.db),
  581. autoload=True)
  582. for table in set_table, reflected:
  583. def roundtrip(store, expected=None):
  584. expected = expected or store
  585. table.insert(store).execute()
  586. row = table.select().execute().first()
  587. try:
  588. self.assert_(list(row) == expected)
  589. except:
  590. print 'Storing %s' % store
  591. print 'Expected %s' % expected
  592. print 'Found %s' % list(row)
  593. raise
  594. table.delete().execute()
  595. roundtrip([None, None, None], [None] * 3)
  596. roundtrip(['', '', ''], [set([''])] * 3)
  597. roundtrip([set(['dq']), set(['a']), set(['5'])])
  598. roundtrip(['dq', 'a', '5'], [set(['dq']), set(['a']),
  599. set(['5'])])
  600. roundtrip([1, 1, 1], [set(['dq']), set(['a']), set(['5'
  601. ])])
  602. roundtrip([set(['dq', 'sq']), None, set(['9', '5', '7'
  603. ])])
  604. set_table.insert().execute({'s3': set(['5'])}, {'s3'
  605. : set(['5', '7'])}, {'s3': set(['5', '7', '9'])},
  606. {'s3': set(['7', '9'])})
  607. rows = select([set_table.c.s3], set_table.c.s3.in_([set(['5'
  608. ]), set(['5', '7']), set(['7', '5'
  609. ])])).execute().fetchall()
  610. found = set([frozenset(row[0]) for row in rows])
  611. eq_(found, set([frozenset(['5']), frozenset(['5', '7'])]))
  612. finally:
  613. meta.drop_all()
  614. @testing.uses_deprecated('Manually quoting ENUM value literals')
  615. def test_enum(self):
  616. """Exercise the ENUM type."""
  617. db = testing.db
  618. enum_table = Table('mysql_enum', MetaData(testing.db),
  619. Column('e1', mysql.ENUM("'a'", "'b'")),
  620. Column('e2', mysql.ENUM("'a'", "'b'"),
  621. nullable=False),
  622. Column('e2generic', Enum("a", "b"),
  623. nullable=False),
  624. Column('e3', mysql.ENUM("'a'", "'b'", strict=True)),
  625. Column('e4', mysql.ENUM("'a'", "'b'", strict=True),
  626. nullable=False),
  627. Column('e5', mysql.ENUM("a", "b")),
  628. Column('e5generic', Enum("a", "b")),
  629. Column('e6', mysql.ENUM("'a'", "b")),
  630. )
  631. eq_(colspec(enum_table.c.e1),
  632. "e1 ENUM('a','b')")
  633. eq_(colspec(enum_table.c.e2),
  634. "e2 ENUM('a','b') NOT NULL")
  635. eq_(colspec(enum_table.c.e2generic),
  636. "e2generic ENUM('a','b') NOT NULL")
  637. eq_(colspec(enum_table.c.e3),
  638. "e3 ENUM('a','b')")
  639. eq_(colspec(enum_table.c.e4),
  640. "e4 ENUM('a','b') NOT NULL")
  641. eq_(colspec(enum_table.c.e5),
  642. "e5 ENUM('a','b')")
  643. eq_(colspec(enum_table.c.e5generic),
  644. "e5generic ENUM('a','b')")
  645. eq_(colspec(enum_table.c.e6),
  646. "e6 ENUM('''a''','b')")
  647. enum_table.drop(checkfirst=True)
  648. enum_table.create()
  649. assert_raises(exc.DBAPIError, enum_table.insert().execute,
  650. e1=None, e2=None, e3=None, e4=None)
  651. assert_raises(exc.StatementError, enum_table.insert().execute,
  652. e1='c', e2='c', e2generic='c', e3='c',
  653. e4='c', e5='c', e5generic='c', e6='c')
  654. enum_table.insert().execute()
  655. enum_table.insert().execute(e1='a', e2='a', e2generic='a', e3='a',
  656. e4='a', e5='a', e5generic='a', e6="'a'")
  657. enum_table.insert().execute(e1='b', e2='b', e2generic='b', e3='b',
  658. e4='b', e5='b', e5generic='b', e6='b')
  659. res = enum_table.select().execute().fetchall()
  660. expected = [(None, 'a', 'a', None, 'a', None, None, None),
  661. ('a', 'a', 'a', 'a', 'a', 'a', 'a', "'a'"),
  662. ('b', 'b', 'b', 'b', 'b', 'b', 'b', 'b')]
  663. # This is known to fail with MySQLDB 1.2.2 beta versions
  664. # which return these as sets.Set(['a']), sets.Set(['b'])
  665. # (even on Pythons with __builtin__.set)
  666. if (testing.against('mysql+mysqldb') and
  667. testing.db.dialect.dbapi.version_info < (1, 2, 2, 'beta', 3) and
  668. testing.db.dialect.dbapi.version_info >= (1, 2, 2)):
  669. # these mysqldb seem to always uses 'sets', even on later pythons
  670. import sets
  671. def convert(value):
  672. if value is None:
  673. return value
  674. if value == '':
  675. return sets.Set([])
  676. else:
  677. return sets.Set([value])
  678. e = []
  679. for row in expected:
  680. e.append(tuple([convert(c) for c in row]))
  681. expected = e
  682. eq_(res, expected)
  683. enum_table.drop()
  684. def test_unicode_enum(self):
  685. unicode_engine = utf8_engine()
  686. metadata = MetaData(unicode_engine)
  687. t1 = Table('table', metadata,
  688. Column('id', Integer, primary_key=True),
  689. Column('value', Enum(u'réveillé', u'drôle', u'S’il')),
  690. Column('value2', mysql.ENUM(u'réveillé', u'drôle', u'S’il'))
  691. )
  692. metadata.create_all()
  693. try:
  694. t1.insert().execute(value=u'drôle', value2=u'drôle')
  695. t1.insert().execute(value=u'réveillé', value2=u'réveillé')
  696. t1.insert().execute(value=u'S’il', value2=u'S’il')
  697. eq_(t1.select().order_by(t1.c.id).execute().fetchall(),
  698. [(1, u'drôle', u'drôle'), (2, u'réveillé', u'réveillé'),
  699. (3, u'S’il', u'S’il')]
  700. )
  701. # test reflection of the enum labels
  702. m2 = MetaData(testing.db)
  703. t2 = Table('table', m2, autoload=True)
  704. # TODO: what's wrong with the last element ? is there
  705. # latin-1 stuff forcing its way in ?
  706. assert t2.c.value.type.enums[0:2] == \
  707. (u'réveillé', u'drôle') #, u'S’il') # eh ?
  708. assert t2.c.value2.type.enums[0:2] == \
  709. (u'réveillé', u'drôle') #, u'S’il') # eh ?
  710. finally:
  711. metadata.drop_all()
  712. def test_enum_compile(self):
  713. e1 = Enum('x', 'y', 'z', name='somename')
  714. t1 = Table('sometable', MetaData(), Column('somecolumn', e1))
  715. self.assert_compile(schema.CreateTable(t1),
  716. "CREATE TABLE sometable (somecolumn "
  717. "ENUM('x','y','z'))")
  718. t1 = Table('sometable', MetaData(), Column('somecolumn',
  719. Enum('x', 'y', 'z', native_enum=False)))
  720. self.assert_compile(schema.CreateTable(t1),
  721. "CREATE TABLE sometable (somecolumn "
  722. "VARCHAR(1), CHECK (somecolumn IN ('x', "
  723. "'y', 'z')))")
  724. @testing.exclude('mysql', '<', (4,), "3.23 can't handle an ENUM of ''")
  725. @testing.uses_deprecated('Manually quoting ENUM value literals')
  726. def test_enum_parse(self):
  727. """More exercises for the ENUM type."""
  728. # MySQL 3.23 can't handle an ENUM of ''....
  729. enum_table = Table('mysql_enum', MetaData(testing.db),
  730. Column('e1', mysql.ENUM("'a'")),
  731. Column('e2', mysql.ENUM("''")),
  732. Column('e3', mysql.ENUM('a')),
  733. Column('e4', mysql.ENUM('')),
  734. Column('e5', mysql.ENUM("'a'", "''")),
  735. Column('e6', mysql.ENUM("''", "'a'")),
  736. Column('e7', mysql.ENUM("''", "'''a'''", "'b''b'", "''''")))
  737. for col in enum_table.c:
  738. self.assert_(repr(col))
  739. try:
  740. enum_table.create()
  741. reflected = Table('mysql_enum', MetaData(testing.db),
  742. autoload=True)
  743. for t in enum_table, reflected:
  744. eq_(t.c.e1.type.enums, ("a",))
  745. eq_(t.c.e2.type.enums, ("",))
  746. eq_(t.c.e3.type.enums, ("a",))
  747. eq_(t.c.e4.type.enums, ("",))
  748. eq_(t.c.e5.type.enums, ("a", ""))
  749. eq_(t.c.e6.type.enums, ("", "a"))
  750. eq_(t.c.e7.type.enums, ("", "'a'", "b'b", "'"))
  751. finally:
  752. enum_table.drop()
  753. class ReflectionTest(fixtures.TestBase, AssertsExecutionResults):
  754. __only_on__ = 'mysql'
  755. def test_default_reflection(self):
  756. """Test reflection of column defaults."""
  757. from sqlalchemy.dialects.mysql import VARCHAR
  758. def_table = Table(
  759. 'mysql_def',
  760. MetaData(testing.db),
  761. Column('c1', VARCHAR(10, collation='utf8_unicode_ci'),
  762. DefaultClause(''), nullable=False),
  763. Column('c2', String(10), DefaultClause('0')),
  764. Column('c3', String(10), DefaultClause('abc')),
  765. Column('c4', TIMESTAMP, DefaultClause('2009-04-05 12:00:00'
  766. )),
  767. Column('c5', TIMESTAMP),
  768. Column('c6', TIMESTAMP,
  769. DefaultClause(sql.text("CURRENT_TIMESTAMP "
  770. "ON UPDATE CURRENT_TIMESTAMP"))),
  771. )
  772. def_table.create()
  773. try:
  774. reflected = Table('mysql_def', MetaData(testing.db),
  775. autoload=True)
  776. finally:
  777. def_table.drop()
  778. assert def_table.c.c1.server_default.arg == ''
  779. assert def_table.c.c2.server_default.arg == '0'
  780. assert def_table.c.c3.server_default.arg == 'abc'
  781. assert def_table.c.c4.server_default.arg \
  782. == '2009-04-05 12:00:00'
  783. assert str(reflected.c.c1.server_default.arg) == "''"
  784. assert str(reflected.c.c2.server_default.arg) == "'0'"
  785. assert str(reflected.c.c3.server_default.arg) == "'abc'"
  786. assert str(reflected.c.c4.server_default.arg) \
  787. == "'2009-04-05 12:00:00'"
  788. assert reflected.c.c5.default is None
  789. assert reflected.c.c5.server_default is None
  790. assert reflected.c.c6.default is None
  791. eq_(
  792. str(reflected.c.c6.server_default.arg).upper(),
  793. "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
  794. )
  795. reflected.create()
  796. try:
  797. reflected2 = Table('mysql_def', MetaData(testing.db),
  798. autoload=True)
  799. finally:
  800. reflected.drop()
  801. assert str(reflected2.c.c1.server_default.arg) == "''"
  802. assert str(reflected2.c.c2.server_default.arg) == "'0'"
  803. assert str(reflected2.c.c3.server_default.arg) == "'abc'"
  804. assert str(reflected2.c.c4.server_default.arg) \
  805. == "'2009-04-05 12:00:00'"
  806. assert reflected.c.c5.default is None
  807. assert reflected.c.c5.server_default is None
  808. assert reflected.c.c6.default is None
  809. eq_(
  810. str(reflected.c.c6.server_default.arg).upper(),
  811. "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
  812. )
  813. def test_reflection_with_table_options(self):
  814. comment = r"""Comment types type speedily ' " \ '' Fun!"""
  815. def_table = Table('mysql_def', MetaData(testing.db),
  816. Column('c1', Integer()),
  817. mysql_engine='MEMORY',
  818. mysql_comment=comment,
  819. mysql_default_charset='utf8',
  820. mysql_auto_increment='5',
  821. mysql_avg_row_length='3',
  822. mysql_password='secret',
  823. mysql_connection='fish',
  824. )
  825. def_table.create()
  826. try:
  827. reflected = Table('mysql_def', MetaData(testing.db),
  828. autoload=True)
  829. finally:
  830. def_table.drop()
  831. assert def_table.kwargs['mysql_engine'] == 'MEMORY'
  832. assert def_table.kwargs['mysql_comment'] == comment
  833. assert def_table.kwargs['mysql_default_charset'] == 'utf8'
  834. assert def_table.kwargs['mysql_auto_increment'] == '5'
  835. assert def_table.kwargs['mysql_avg_row_length'] == '3'
  836. assert def_table.kwargs['mysql_password'] == 'secret'
  837. assert def_table.kwargs['mysql_connection'] == 'fish'
  838. assert reflected.kwargs['mysql_engine'] == 'MEMORY'
  839. assert reflected.kwargs['mysql_comment'] == comment
  840. assert reflected.kwargs['mysql_default charset'] == 'utf8'
  841. assert reflected.kwargs['mysql_avg_row_length'] == '3'
  842. assert reflected.kwargs['mysql_connection'] == 'fish'
  843. # This field doesn't seem to be returned by mysql itself.
  844. #assert reflected.kwargs['mysql_password'] == 'secret'
  845. # This is explicitly ignored when reflecting schema.
  846. #assert reflected.kwargs['mysql_auto_increment'] == '5'
  847. def test_reflection_on_include_columns(self):
  848. """Test reflection of include_columns to be sure they respect case."""
  849. case_table = Table('mysql_case', MetaData(testing.db),
  850. Column('c1', String(10)),
  851. Column('C2', String(10)),
  852. Column('C3', String(10)))
  853. try:
  854. case_table.create()
  855. reflected = Table('mysql_case', MetaData(testing.db),
  856. autoload=True, include_columns=['c1', 'C2'])
  857. for t in case_table, reflected:
  858. assert 'c1' in t.c.keys()
  859. assert 'C2' in t.c.keys()
  860. reflected2 = Table('mysql_case', MetaData(testing.db),
  861. autoload=True, include_columns=['c1', 'c2'])
  862. assert 'c1' in reflected2.c.keys()
  863. for c in ['c2', 'C2', 'C3']:
  864. assert c not in reflected2.c.keys()
  865. finally:
  866. case_table.drop()
  867. @testing.exclude('mysql', '<', (5, 0, 0), 'early types are squirrely')
  868. @testing.uses_deprecated('Using String type with no length')
  869. @testing.uses_deprecated('Manually quoting ENUM value literals')
  870. def test_type_reflection(self):
  871. # (ask_for, roundtripped_as_if_different)
  872. specs = [( String(1), mysql.MSString(1), ),
  873. ( String(3), mysql.MSString(3), ),
  874. ( Text(), mysql.MSText(), ),
  875. ( Unicode(1), mysql.MSString(1), ),
  876. ( Unicode(3), mysql.MSString(3), ),
  877. ( UnicodeText(), mysql.MSText(), ),
  878. ( mysql.MSChar(1), ),
  879. ( mysql.MSChar(3), ),
  880. ( NCHAR(2), mysql.MSChar(2), ),
  881. ( mysql.MSNChar(2), mysql.MSChar(2), ), # N is CREATE only
  882. ( mysql.MSNVarChar(22), mysql.MSString(22), ),
  883. ( SmallInteger(), mysql.MSSmallInteger(), ),
  884. ( SmallInteger(), mysql.MSSmallInteger(4), ),
  885. ( mysql.MSSmallInteger(), ),
  886. ( mysql.MSSmallInteger(4), mysql.MSSmallInteger(4), ),
  887. ( mysql.MSMediumInteger(), mysql.MSMediumInteger(), ),
  888. ( mysql.MSMediumInteger(8), mysql.MSMediumInteger(8), ),
  889. ( LargeBinary(3), mysql.TINYBLOB(), ),
  890. ( LargeBinary(), mysql.BLOB() ),
  891. ( mysql.MSBinary(3), mysql.MSBinary(3), ),
  892. ( mysql.MSVarBinary(3),),
  893. ( mysql.MSTinyBlob(),),
  894. ( mysql.MSBlob(),),
  895. ( mysql.MSBlob(1234), mysql.MSBlob()),
  896. ( mysql.MSMediumBlob(),),
  897. ( mysql.MSLongBlob(),),
  898. ( mysql.ENUM("''","'fleem'"), ),
  899. ]
  900. columns = [Column('c%i' % (i + 1), t[0]) for i, t in enumerate(specs)]
  901. db = testing.db
  902. m = MetaData(db)
  903. t_table = Table('mysql_types', m, *columns)
  904. try:
  905. m.create_all()
  906. m2 = MetaData(db)
  907. rt = Table('mysql_types', m2, autoload=True)
  908. try:
  909. db.execute('CREATE OR REPLACE VIEW mysql_types_v '
  910. 'AS SELECT * from mysql_types')
  911. rv = Table('mysql_types_v', m2, autoload=True)
  912. expected = [len(c) > 1 and c[1] or c[0] for c in specs]
  913. # Early 5.0 releases seem to report more "general" for columns
  914. # in a view, e.g. char -> varchar, tinyblob -> mediumblob
  915. #
  916. # Not sure exactly which point version has the fix.
  917. if db.dialect.server_version_info < (5, 0, 11):
  918. tables = rt,
  919. else:
  920. tables = rt, rv
  921. for table in tables:
  922. for i, reflected in enumerate(table.c):
  923. assert isinstance(reflected.type,
  924. type(expected[i])), \
  925. 'element %d: %r not instance of %r' % (i,
  926. reflected.type, type(expected[i]))
  927. finally:
  928. db.execute('DROP VIEW mysql_types_v')
  929. finally:
  930. m.drop_all()
  931. def test_autoincrement(self):
  932. meta = MetaData(testing.db)
  933. try:
  934. Table('ai_1', meta,
  935. Column('int_y', Integer, primary_key=True),
  936. Column('int_n', Integer, DefaultClause('0'),
  937. primary_key=True),
  938. mysql_engine='MyISAM')
  939. Table('ai_2', meta,
  940. Column('int_y', Integer, primary_key=True),
  941. Column('int_n', Integer, DefaultClause('0'),
  942. primary_key=True),
  943. mysql_engine='MyISAM')
  944. Table('ai_3', meta,
  945. Column('int_n', Integer, DefaultClause('0'),
  946. primary_key=True, autoincrement=False),
  947. Column('int_y', Integer, primary_key=True),
  948. mysql_engine='MyISAM')
  949. Table('ai_4', meta,
  950. Column('int_n', Integer, DefaultClause('0'),
  951. primary_key=True, autoincrement=False),
  952. Column('int_n2', Integer, DefaultClause('0'),
  953. primary_key=True, autoincrement=False),
  954. mysql_engine='MyISAM')
  955. Table('ai_5', meta,
  956. Column('int_y', Integer, primary_key=True),
  957. Column('int_n', Integer, DefaultClause('0'),
  958. primary_key=True, autoincrement=False),
  959. mysql_engine='MyISAM')
  960. Table('ai_6', meta,
  961. Column('o1', String(1), DefaultClause('x'),
  962. primary_key=True),
  963. Column('int_y', Integer, primary_key=True),
  964. mysql_engine='MyISAM')
  965. Table('ai_7', meta,
  966. Column('o1', String(1), DefaultClause('x'),
  967. primary_key=True),
  968. Column('o2', String(1), DefaultClause('x'),
  969. primary_key=True),
  970. Column('int_y', Integer, primary_key=True),
  971. mysql_engine='MyISAM')
  972. Table('ai_8', meta,
  973. Column('o1', String(1), DefaultClause('x'),
  974. primary_key=True),
  975. Column('o2', String(1), DefaultClause('x'),
  976. primary_key=True),
  977. mysql_engine='MyISAM')
  978. meta.create_all()
  979. table_names = ['ai_1', 'ai_2', 'ai_3', 'ai_4',
  980. 'ai_5', 'ai_6', 'ai_7', 'ai_8']
  981. mr = MetaData(testing.db)
  982. mr.reflect(only=table_names)
  983. for tbl in [mr.tables[name] for name in table_names]:
  984. for c in tbl.c:
  985. if c.name.startswith('int_y'):
  986. assert c.autoincrement
  987. elif c.name.startswith('int_n'):
  988. assert not c.autoincrement
  989. tbl.insert().execute()
  990. if 'int_y' in tbl.c:
  991. assert select([tbl.c.int_y]).scalar() == 1
  992. assert list(tbl.select().execute().first()).count(1) == 1
  993. else:
  994. assert 1 not in list(tbl.select().execute().first())
  995. finally:
  996. meta.drop_all()
  997. @testing.exclude('mysql', '<', (5, 0, 0), 'no information_schema support')
  998. def test_system_views(self):
  999. dialect = testing.db.dialect
  1000. connection = testing.db.connect()
  1001. view_names = dialect.get_view_names(connection, "information_schema")
  1002. self.assert_('TABLES' in view_names)
  1003. class SQLTest(fixtures.TestBase, AssertsCompiledSQL):
  1004. """Tests MySQL-dialect specific compilation."""
  1005. __dialect__ = mysql.dialect()
  1006. def test_precolumns(self):
  1007. dialect = self.__dialect__
  1008. def gen(distinct=None, prefixes=None):
  1009. kw = {}
  1010. if distinct is not None:
  1011. kw['distinct'] = distinct
  1012. if prefixes is not None:
  1013. kw['prefixes'] = prefixes
  1014. return str(select(['q'], **kw).compile(dialect=dialect))
  1015. eq_(gen(None), 'SELECT q')
  1016. eq_(gen(True), 'SELECT DISTINCT q')
  1017. assert_raises(
  1018. exc.SADeprecationWarning,
  1019. gen, 'DISTINCT'
  1020. )
  1021. eq_(gen(prefixes=['ALL']), 'SELECT ALL q')
  1022. eq_(gen(prefixes=['DISTINCTROW']),
  1023. 'SELECT DISTINCTROW q')
  1024. # Interaction with MySQL prefix extensions
  1025. eq_(
  1026. gen(None, ['straight_join']),
  1027. 'SELECT straight_join q')
  1028. eq_(
  1029. gen(False, ['HIGH_PRIORITY', 'SQL_SMALL_RESULT', 'ALL']),
  1030. 'SELECT HIGH_PRIORITY SQL_SMALL_RESULT ALL q')
  1031. eq_(
  1032. gen(True, ['high_priority', sql.text('sql_cache')]),
  1033. 'SELECT high_priority sql_cache DISTINCT q')
  1034. @testing.uses_deprecated
  1035. def test_deprecated_distinct(self):
  1036. dialect = self.__dialect__
  1037. self.assert_compile(
  1038. select(['q'], distinct='ALL'),
  1039. 'SELECT ALL q',
  1040. )
  1041. self.assert_compile(
  1042. select(['q'], distinct='distinctROW'),
  1043. 'SELECT DISTINCTROW q',
  1044. )
  1045. self.assert_compile(
  1046. select(['q'], distinct='ALL',
  1047. prefixes=['HIGH_PRIORITY', 'SQL_SMALL_RESULT']),
  1048. 'SELECT HIGH_PRIORITY SQL_SMALL_RESULT ALL q'
  1049. )
  1050. def test_backslash_escaping(self):
  1051. self.assert_compile(
  1052. sql.column('foo').like('bar', escape='\\'),
  1053. "foo LIKE %s ESCAPE '\\\\'"
  1054. )
  1055. dialect = mysql.dialect()
  1056. dialect._backslash_escapes=False
  1057. self.assert_compile(
  1058. sql.column('foo').like('bar', escape='\\'),
  1059. "foo LIKE %s ESCAPE '\\'",
  1060. dialect=dialect
  1061. )
  1062. def test_limit(self):
  1063. t = sql.table('t', sql.column('col1'), sql.column('col2'))
  1064. self.assert_compile(
  1065. select([t]).limit(10).offset(20),
  1066. "SELECT t.col1, t.col2 FROM t LIMIT %s, %s",
  1067. {'param_1':20, 'param_2':10}
  1068. )
  1069. self.assert_compile(
  1070. select([t]).limit(10),
  1071. "SELECT t.col1, t.col2 FROM t LIMIT %s",
  1072. {'param_1':10})
  1073. self.assert_compile(
  1074. select([t]).offset(10),
  1075. "SELECT t.col1, t.col2 FROM t LIMIT %s, 18446744073709551615",
  1076. {'param_1':10}
  1077. )
  1078. def test_varchar_raise(self):
  1079. for type_ in (
  1080. String,
  1081. VARCHAR,
  1082. String(),
  1083. VARCHAR(),
  1084. NVARCHAR(),

Large files files are truncated, but you can click here to view the full file