~ubuntu-branches/debian/jessie/sqlalchemy/jessie

« back to all changes in this revision

Viewing changes to test/dialect/test_mysql.py

  • Committer: Package Import Robot
  • Author(s): Piotr Ożarowski, Jakub Wilk, Piotr Ożarowski
  • Date: 2013-07-06 20:53:52 UTC
  • mfrom: (1.4.23) (16.1.17 experimental)
  • Revision ID: package-import@ubuntu.com-20130706205352-ryppl1eto3illd79
Tags: 0.8.2-1
[ Jakub Wilk ]
* Use canonical URIs for Vcs-* fields.

[ Piotr Ożarowski ]
* New upstream release
* Upload to unstable
* Build depend on python3-all instead of -dev, extensions are not built for
  Python 3.X 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# coding: utf-8
2
 
 
3
 
from test.lib.testing import eq_, assert_raises, assert_raises_message
4
 
 
5
 
 
6
 
from sqlalchemy import *
7
 
from sqlalchemy import sql, exc, schema, types as sqltypes
8
 
from sqlalchemy.dialects.mysql import base as mysql
9
 
from sqlalchemy.engine.url import make_url
10
 
from test.lib import fixtures, AssertsCompiledSQL, AssertsExecutionResults
11
 
from test.lib import testing, engines
12
 
from test.lib.engines import utf8_engine
13
 
import datetime
14
 
 
15
 
class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
16
 
 
17
 
    __dialect__ = mysql.dialect()
18
 
 
19
 
    def test_reserved_words(self):
20
 
        table = Table("mysql_table", MetaData(),
21
 
            Column("col1", Integer),
22
 
            Column("master_ssl_verify_server_cert", Integer))
23
 
        x = select([table.c.col1, table.c.master_ssl_verify_server_cert])
24
 
 
25
 
        self.assert_compile(x,
26
 
            '''SELECT mysql_table.col1, mysql_table.`master_ssl_verify_server_cert` FROM mysql_table''')
27
 
 
28
 
    def test_create_index_simple(self):
29
 
        m = MetaData()
30
 
        tbl = Table('testtbl', m, Column('data', String(255)))
31
 
        idx = Index('test_idx1', tbl.c.data)
32
 
 
33
 
        self.assert_compile(schema.CreateIndex(idx),
34
 
            'CREATE INDEX test_idx1 ON testtbl (data)',
35
 
            dialect=mysql.dialect())
36
 
 
37
 
    def test_create_index_with_length(self):
38
 
        m = MetaData()
39
 
        tbl = Table('testtbl', m, Column('data', String(255)))
40
 
        idx1 = Index('test_idx1', tbl.c.data, mysql_length=10)
41
 
        idx2 = Index('test_idx2', tbl.c.data, mysql_length=5)
42
 
 
43
 
        self.assert_compile(schema.CreateIndex(idx1),
44
 
            'CREATE INDEX test_idx1 ON testtbl (data(10))',
45
 
            dialect=mysql.dialect())
46
 
        self.assert_compile(schema.CreateIndex(idx2),
47
 
            'CREATE INDEX test_idx2 ON testtbl (data(5))',
48
 
            dialect=mysql.dialect())
49
 
 
50
 
    def test_create_index_with_using(self):
51
 
        m = MetaData()
52
 
        tbl = Table('testtbl', m, Column('data', String(255)))
53
 
        idx1 = Index('test_idx1', tbl.c.data, mysql_using='btree')
54
 
        idx2 = Index('test_idx2', tbl.c.data, mysql_using='hash')
55
 
 
56
 
        self.assert_compile(schema.CreateIndex(idx1),
57
 
            'CREATE INDEX test_idx1 ON testtbl (data) USING btree',
58
 
            dialect=mysql.dialect())
59
 
        self.assert_compile(schema.CreateIndex(idx2),
60
 
            'CREATE INDEX test_idx2 ON testtbl (data) USING hash',
61
 
            dialect=mysql.dialect())
62
 
 
63
 
    def test_create_pk_plain(self):
64
 
        m = MetaData()
65
 
        tbl = Table('testtbl', m, Column('data', String(255)),
66
 
            PrimaryKeyConstraint('data'))
67
 
 
68
 
        self.assert_compile(schema.CreateTable(tbl),
69
 
            "CREATE TABLE testtbl (data VARCHAR(255), PRIMARY KEY (data))",
70
 
            dialect=mysql.dialect())
71
 
 
72
 
    def test_create_pk_with_using(self):
73
 
        m = MetaData()
74
 
        tbl = Table('testtbl', m, Column('data', String(255)),
75
 
            PrimaryKeyConstraint('data', mysql_using='btree'))
76
 
 
77
 
        self.assert_compile(schema.CreateTable(tbl),
78
 
            "CREATE TABLE testtbl (data VARCHAR(255), "
79
 
            "PRIMARY KEY (data) USING btree)",
80
 
            dialect=mysql.dialect())
81
 
 
82
 
class DialectTest(fixtures.TestBase):
83
 
    __only_on__ = 'mysql'
84
 
 
85
 
    @testing.only_on(['mysql+mysqldb', 'mysql+oursql'],
86
 
                    'requires particular SSL arguments')
87
 
    def test_ssl_arguments(self):
88
 
        dialect = testing.db.dialect
89
 
        kwarg = dialect.create_connect_args(
90
 
            make_url("mysql://scott:tiger@localhost:3306/test"
91
 
                "?ssl_ca=/ca.pem&ssl_cert=/cert.pem&ssl_key=/key.pem")
92
 
        )[1]
93
 
        # args that differ among mysqldb and oursql
94
 
        for k in ('use_unicode', 'found_rows', 'client_flag'):
95
 
            kwarg.pop(k, None)
96
 
        eq_(
97
 
            kwarg,
98
 
            {
99
 
                'passwd': 'tiger', 'db': 'test',
100
 
                'ssl': {'ca': '/ca.pem', 'cert': '/cert.pem',
101
 
                        'key': '/key.pem'},
102
 
                'host': 'localhost', 'user': 'scott',
103
 
                'port': 3306
104
 
            }
105
 
        )
106
 
 
107
 
 
108
 
class TypesTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiledSQL):
109
 
    "Test MySQL column types"
110
 
 
111
 
    __only_on__ = 'mysql'
112
 
    __dialect__ = mysql.dialect()
113
 
 
114
 
    @testing.uses_deprecated('Manually quoting ENUM value literals')
115
 
    def test_basic(self):
116
 
        meta1 = MetaData(testing.db)
117
 
        table = Table(
118
 
            'mysql_types', meta1,
119
 
            Column('id', Integer, primary_key=True),
120
 
            Column('num1', mysql.MSInteger(unsigned=True)),
121
 
            Column('text1', mysql.MSLongText),
122
 
            Column('text2', mysql.MSLongText()),
123
 
            Column('num2', mysql.MSBigInteger),
124
 
            Column('num3', mysql.MSBigInteger()),
125
 
            Column('num4', mysql.MSDouble),
126
 
            Column('num5', mysql.MSDouble()),
127
 
            Column('num6', mysql.MSMediumInteger),
128
 
            Column('enum1', mysql.ENUM("'black'", "'white'")),
129
 
            Column('enum2', mysql.ENUM("dog", "cat")),
130
 
            )
131
 
        try:
132
 
            table.drop(checkfirst=True)
133
 
            table.create()
134
 
            meta2 = MetaData(testing.db)
135
 
            t2 = Table('mysql_types', meta2, autoload=True)
136
 
            assert isinstance(t2.c.num1.type, mysql.MSInteger)
137
 
            assert t2.c.num1.type.unsigned
138
 
            assert isinstance(t2.c.text1.type, mysql.MSLongText)
139
 
            assert isinstance(t2.c.text2.type, mysql.MSLongText)
140
 
            assert isinstance(t2.c.num2.type, mysql.MSBigInteger)
141
 
            assert isinstance(t2.c.num3.type, mysql.MSBigInteger)
142
 
            assert isinstance(t2.c.num4.type, mysql.MSDouble)
143
 
            assert isinstance(t2.c.num5.type, mysql.MSDouble)
144
 
            assert isinstance(t2.c.num6.type, mysql.MSMediumInteger)
145
 
            assert isinstance(t2.c.enum1.type, mysql.ENUM)
146
 
            assert isinstance(t2.c.enum2.type, mysql.ENUM)
147
 
            t2.drop()
148
 
            t2.create()
149
 
        finally:
150
 
            meta1.drop_all()
151
 
 
152
 
    def test_numeric(self):
153
 
        "Exercise type specification and options for numeric types."
154
 
 
155
 
        columns = [
156
 
            # column type, args, kwargs, expected ddl
157
 
            # e.g. Column(Integer(10, unsigned=True)) ==
158
 
            # 'INTEGER(10) UNSIGNED'
159
 
            (mysql.MSNumeric, [], {},
160
 
             'NUMERIC'),
161
 
            (mysql.MSNumeric, [None], {},
162
 
             'NUMERIC'),
163
 
            (mysql.MSNumeric, [12], {},
164
 
             'NUMERIC(12)'),
165
 
            (mysql.MSNumeric, [12, 4], {'unsigned':True},
166
 
             'NUMERIC(12, 4) UNSIGNED'),
167
 
            (mysql.MSNumeric, [12, 4], {'zerofill':True},
168
 
             'NUMERIC(12, 4) ZEROFILL'),
169
 
            (mysql.MSNumeric, [12, 4], {'zerofill':True, 'unsigned':True},
170
 
             'NUMERIC(12, 4) UNSIGNED ZEROFILL'),
171
 
 
172
 
            (mysql.MSDecimal, [], {},
173
 
             'DECIMAL'),
174
 
            (mysql.MSDecimal, [None], {},
175
 
             'DECIMAL'),
176
 
            (mysql.MSDecimal, [12], {},
177
 
             'DECIMAL(12)'),
178
 
            (mysql.MSDecimal, [12, None], {},
179
 
             'DECIMAL(12)'),
180
 
            (mysql.MSDecimal, [12, 4], {'unsigned':True},
181
 
             'DECIMAL(12, 4) UNSIGNED'),
182
 
            (mysql.MSDecimal, [12, 4], {'zerofill':True},
183
 
             'DECIMAL(12, 4) ZEROFILL'),
184
 
            (mysql.MSDecimal, [12, 4], {'zerofill':True, 'unsigned':True},
185
 
             'DECIMAL(12, 4) UNSIGNED ZEROFILL'),
186
 
 
187
 
            (mysql.MSDouble, [None, None], {},
188
 
             'DOUBLE'),
189
 
            (mysql.MSDouble, [12, 4], {'unsigned':True},
190
 
             'DOUBLE(12, 4) UNSIGNED'),
191
 
            (mysql.MSDouble, [12, 4], {'zerofill':True},
192
 
             'DOUBLE(12, 4) ZEROFILL'),
193
 
            (mysql.MSDouble, [12, 4], {'zerofill':True, 'unsigned':True},
194
 
             'DOUBLE(12, 4) UNSIGNED ZEROFILL'),
195
 
 
196
 
            (mysql.MSReal, [None, None], {},
197
 
             'REAL'),
198
 
            (mysql.MSReal, [12, 4], {'unsigned':True},
199
 
             'REAL(12, 4) UNSIGNED'),
200
 
            (mysql.MSReal, [12, 4], {'zerofill':True},
201
 
             'REAL(12, 4) ZEROFILL'),
202
 
            (mysql.MSReal, [12, 4], {'zerofill':True, 'unsigned':True},
203
 
             'REAL(12, 4) UNSIGNED ZEROFILL'),
204
 
 
205
 
            (mysql.MSFloat, [], {},
206
 
             'FLOAT'),
207
 
            (mysql.MSFloat, [None], {},
208
 
             'FLOAT'),
209
 
            (mysql.MSFloat, [12], {},
210
 
             'FLOAT(12)'),
211
 
            (mysql.MSFloat, [12, 4], {},
212
 
             'FLOAT(12, 4)'),
213
 
            (mysql.MSFloat, [12, 4], {'unsigned':True},
214
 
             'FLOAT(12, 4) UNSIGNED'),
215
 
            (mysql.MSFloat, [12, 4], {'zerofill':True},
216
 
             'FLOAT(12, 4) ZEROFILL'),
217
 
            (mysql.MSFloat, [12, 4], {'zerofill':True, 'unsigned':True},
218
 
             'FLOAT(12, 4) UNSIGNED ZEROFILL'),
219
 
 
220
 
            (mysql.MSInteger, [], {},
221
 
             'INTEGER'),
222
 
            (mysql.MSInteger, [4], {},
223
 
             'INTEGER(4)'),
224
 
            (mysql.MSInteger, [4], {'unsigned':True},
225
 
             'INTEGER(4) UNSIGNED'),
226
 
            (mysql.MSInteger, [4], {'zerofill':True},
227
 
             'INTEGER(4) ZEROFILL'),
228
 
            (mysql.MSInteger, [4], {'zerofill':True, 'unsigned':True},
229
 
             'INTEGER(4) UNSIGNED ZEROFILL'),
230
 
 
231
 
            (mysql.MSBigInteger, [], {},
232
 
             'BIGINT'),
233
 
            (mysql.MSBigInteger, [4], {},
234
 
             'BIGINT(4)'),
235
 
            (mysql.MSBigInteger, [4], {'unsigned':True},
236
 
             'BIGINT(4) UNSIGNED'),
237
 
            (mysql.MSBigInteger, [4], {'zerofill':True},
238
 
             'BIGINT(4) ZEROFILL'),
239
 
            (mysql.MSBigInteger, [4], {'zerofill':True, 'unsigned':True},
240
 
             'BIGINT(4) UNSIGNED ZEROFILL'),
241
 
 
242
 
             (mysql.MSMediumInteger, [], {},
243
 
              'MEDIUMINT'),
244
 
             (mysql.MSMediumInteger, [4], {},
245
 
              'MEDIUMINT(4)'),
246
 
             (mysql.MSMediumInteger, [4], {'unsigned':True},
247
 
              'MEDIUMINT(4) UNSIGNED'),
248
 
             (mysql.MSMediumInteger, [4], {'zerofill':True},
249
 
              'MEDIUMINT(4) ZEROFILL'),
250
 
             (mysql.MSMediumInteger, [4], {'zerofill':True, 'unsigned':True},
251
 
              'MEDIUMINT(4) UNSIGNED ZEROFILL'),
252
 
 
253
 
            (mysql.MSTinyInteger, [], {},
254
 
             'TINYINT'),
255
 
            (mysql.MSTinyInteger, [1], {},
256
 
             'TINYINT(1)'),
257
 
            (mysql.MSTinyInteger, [1], {'unsigned':True},
258
 
             'TINYINT(1) UNSIGNED'),
259
 
            (mysql.MSTinyInteger, [1], {'zerofill':True},
260
 
             'TINYINT(1) ZEROFILL'),
261
 
            (mysql.MSTinyInteger, [1], {'zerofill':True, 'unsigned':True},
262
 
             'TINYINT(1) UNSIGNED ZEROFILL'),
263
 
 
264
 
            (mysql.MSSmallInteger, [], {},
265
 
             'SMALLINT'),
266
 
            (mysql.MSSmallInteger, [4], {},
267
 
             'SMALLINT(4)'),
268
 
            (mysql.MSSmallInteger, [4], {'unsigned':True},
269
 
             'SMALLINT(4) UNSIGNED'),
270
 
            (mysql.MSSmallInteger, [4], {'zerofill':True},
271
 
             'SMALLINT(4) ZEROFILL'),
272
 
            (mysql.MSSmallInteger, [4], {'zerofill':True, 'unsigned':True},
273
 
             'SMALLINT(4) UNSIGNED ZEROFILL'),
274
 
           ]
275
 
 
276
 
        table_args = ['test_mysql_numeric', MetaData(testing.db)]
277
 
        for index, spec in enumerate(columns):
278
 
            type_, args, kw, res = spec
279
 
            table_args.append(Column('c%s' % index, type_(*args, **kw)))
280
 
 
281
 
        numeric_table = Table(*table_args)
282
 
        gen = testing.db.dialect.ddl_compiler(testing.db.dialect, None)
283
 
 
284
 
        for col in numeric_table.c:
285
 
            index = int(col.name[1:])
286
 
            eq_(gen.get_column_specification(col),
287
 
                           "%s %s" % (col.name, columns[index][3]))
288
 
            self.assert_(repr(col))
289
 
 
290
 
        try:
291
 
            numeric_table.create(checkfirst=True)
292
 
            assert True
293
 
        except:
294
 
            raise
295
 
        numeric_table.drop()
296
 
 
297
 
    @testing.exclude('mysql', '<', (4, 1, 1), 'no charset support')
298
 
    def test_charset(self):
299
 
        """Exercise CHARACTER SET and COLLATE-ish options on string types."""
300
 
 
301
 
        columns = [
302
 
            (mysql.MSChar, [1], {},
303
 
             'CHAR(1)'),
304
 
             (mysql.NCHAR, [1], {},
305
 
              'NATIONAL CHAR(1)'),
306
 
            (mysql.MSChar, [1], {'binary':True},
307
 
             'CHAR(1) BINARY'),
308
 
            (mysql.MSChar, [1], {'ascii':True},
309
 
             'CHAR(1) ASCII'),
310
 
            (mysql.MSChar, [1], {'unicode':True},
311
 
             'CHAR(1) UNICODE'),
312
 
            (mysql.MSChar, [1], {'ascii':True, 'binary':True},
313
 
             'CHAR(1) ASCII BINARY'),
314
 
            (mysql.MSChar, [1], {'unicode':True, 'binary':True},
315
 
             'CHAR(1) UNICODE BINARY'),
316
 
            (mysql.MSChar, [1], {'charset':'utf8'},
317
 
             'CHAR(1) CHARACTER SET utf8'),
318
 
            (mysql.MSChar, [1], {'charset':'utf8', 'binary':True},
319
 
             'CHAR(1) CHARACTER SET utf8 BINARY'),
320
 
            (mysql.MSChar, [1], {'charset':'utf8', 'unicode':True},
321
 
             'CHAR(1) CHARACTER SET utf8'),
322
 
            (mysql.MSChar, [1], {'charset':'utf8', 'ascii':True},
323
 
             'CHAR(1) CHARACTER SET utf8'),
324
 
            (mysql.MSChar, [1], {'collation': 'utf8_bin'},
325
 
             'CHAR(1) COLLATE utf8_bin'),
326
 
            (mysql.MSChar, [1], {'charset': 'utf8', 'collation': 'utf8_bin'},
327
 
             'CHAR(1) CHARACTER SET utf8 COLLATE utf8_bin'),
328
 
            (mysql.MSChar, [1], {'charset': 'utf8', 'binary': True},
329
 
             'CHAR(1) CHARACTER SET utf8 BINARY'),
330
 
            (mysql.MSChar, [1], {'charset': 'utf8', 'collation': 'utf8_bin',
331
 
                              'binary': True},
332
 
             'CHAR(1) CHARACTER SET utf8 COLLATE utf8_bin'),
333
 
            (mysql.MSChar, [1], {'national':True},
334
 
             'NATIONAL CHAR(1)'),
335
 
            (mysql.MSChar, [1], {'national':True, 'charset':'utf8'},
336
 
             'NATIONAL CHAR(1)'),
337
 
            (mysql.MSChar, [1], {'national':True, 'charset':'utf8',
338
 
                                'binary':True},
339
 
             'NATIONAL CHAR(1) BINARY'),
340
 
            (mysql.MSChar, [1], {'national':True, 'binary':True,
341
 
                                'unicode':True},
342
 
             'NATIONAL CHAR(1) BINARY'),
343
 
            (mysql.MSChar, [1], {'national':True, 'collation':'utf8_bin'},
344
 
             'NATIONAL CHAR(1) COLLATE utf8_bin'),
345
 
 
346
 
            (mysql.MSString, [1], {'charset':'utf8', 'collation':'utf8_bin'},
347
 
             'VARCHAR(1) CHARACTER SET utf8 COLLATE utf8_bin'),
348
 
            (mysql.MSString, [1], {'national':True, 'collation':'utf8_bin'},
349
 
             'NATIONAL VARCHAR(1) COLLATE utf8_bin'),
350
 
 
351
 
            (mysql.MSTinyText, [], {'charset':'utf8', 'collation':'utf8_bin'},
352
 
             'TINYTEXT CHARACTER SET utf8 COLLATE utf8_bin'),
353
 
 
354
 
            (mysql.MSMediumText, [], {'charset':'utf8', 'binary':True},
355
 
             'MEDIUMTEXT CHARACTER SET utf8 BINARY'),
356
 
 
357
 
            (mysql.MSLongText, [], {'ascii':True},
358
 
             'LONGTEXT ASCII'),
359
 
 
360
 
            (mysql.ENUM, ["foo", "bar"], {'unicode':True},
361
 
             '''ENUM('foo','bar') UNICODE''')
362
 
           ]
363
 
 
364
 
        table_args = ['test_mysql_charset', MetaData(testing.db)]
365
 
        for index, spec in enumerate(columns):
366
 
            type_, args, kw, res = spec
367
 
            table_args.append(Column('c%s' % index, type_(*args, **kw)))
368
 
 
369
 
        charset_table = Table(*table_args)
370
 
        gen = testing.db.dialect.ddl_compiler(testing.db.dialect, None)
371
 
 
372
 
        for col in charset_table.c:
373
 
            index = int(col.name[1:])
374
 
            eq_(gen.get_column_specification(col),
375
 
                           "%s %s" % (col.name, columns[index][3]))
376
 
            self.assert_(repr(col))
377
 
 
378
 
        try:
379
 
            charset_table.create(checkfirst=True)
380
 
            assert True
381
 
        except:
382
 
            raise
383
 
        charset_table.drop()
384
 
 
385
 
    @testing.exclude('mysql', '<', (5, 0, 5), 'a 5.0+ feature')
386
 
    @testing.provide_metadata
387
 
    def test_charset_collate_table(self):
388
 
        t = Table('foo', self.metadata,
389
 
            Column('id', Integer),
390
 
            mysql_default_charset='utf8',
391
 
            mysql_collate='utf8_unicode_ci'
392
 
        )
393
 
        t.create()
394
 
        m2 = MetaData(testing.db)
395
 
        t2 = Table('foo', m2, autoload=True)
396
 
        eq_(t2.kwargs['mysql_collate'], 'utf8_unicode_ci')
397
 
        eq_(t2.kwargs['mysql_default charset'], 'utf8')
398
 
 
399
 
    @testing.exclude('mysql', '<', (5, 0, 5), 'a 5.0+ feature')
400
 
    @testing.fails_on('mysql+oursql', 'some round trips fail, oursql bug ?')
401
 
    def test_bit_50(self):
402
 
        """Exercise BIT types on 5.0+ (not valid for all engine types)"""
403
 
 
404
 
        meta = MetaData(testing.db)
405
 
        bit_table = Table('mysql_bits', meta,
406
 
                          Column('b1', mysql.MSBit),
407
 
                          Column('b2', mysql.MSBit()),
408
 
                          Column('b3', mysql.MSBit(), nullable=False),
409
 
                          Column('b4', mysql.MSBit(1)),
410
 
                          Column('b5', mysql.MSBit(8)),
411
 
                          Column('b6', mysql.MSBit(32)),
412
 
                          Column('b7', mysql.MSBit(63)),
413
 
                          Column('b8', mysql.MSBit(64)))
414
 
 
415
 
        eq_(colspec(bit_table.c.b1), 'b1 BIT')
416
 
        eq_(colspec(bit_table.c.b2), 'b2 BIT')
417
 
        eq_(colspec(bit_table.c.b3), 'b3 BIT NOT NULL')
418
 
        eq_(colspec(bit_table.c.b4), 'b4 BIT(1)')
419
 
        eq_(colspec(bit_table.c.b5), 'b5 BIT(8)')
420
 
        eq_(colspec(bit_table.c.b6), 'b6 BIT(32)')
421
 
        eq_(colspec(bit_table.c.b7), 'b7 BIT(63)')
422
 
        eq_(colspec(bit_table.c.b8), 'b8 BIT(64)')
423
 
 
424
 
        for col in bit_table.c:
425
 
            self.assert_(repr(col))
426
 
        try:
427
 
            meta.create_all()
428
 
 
429
 
            meta2 = MetaData(testing.db)
430
 
            reflected = Table('mysql_bits', meta2, autoload=True)
431
 
 
432
 
            for table in bit_table, reflected:
433
 
 
434
 
                def roundtrip(store, expected=None):
435
 
                    expected = expected or store
436
 
                    table.insert(store).execute()
437
 
                    row = table.select().execute().first()
438
 
                    try:
439
 
                        self.assert_(list(row) == expected)
440
 
                    except:
441
 
                        print "Storing %s" % store
442
 
                        print "Expected %s" % expected
443
 
                        print "Found %s" % list(row)
444
 
                        raise
445
 
                    table.delete().execute().close()
446
 
 
447
 
                roundtrip([0] * 8)
448
 
                roundtrip([None, None, 0, None, None, None, None, None])
449
 
                roundtrip([1] * 8)
450
 
                roundtrip([sql.text("b'1'")] * 8, [1] * 8)
451
 
 
452
 
                i = 255
453
 
                roundtrip([0, 0, 0, 0, i, i, i, i])
454
 
                i = 2**32 - 1
455
 
                roundtrip([0, 0, 0, 0, 0, i, i, i])
456
 
                i = 2**63 - 1
457
 
                roundtrip([0, 0, 0, 0, 0, 0, i, i])
458
 
                i = 2**64 - 1
459
 
                roundtrip([0, 0, 0, 0, 0, 0, 0, i])
460
 
        finally:
461
 
            meta.drop_all()
462
 
 
463
 
    def test_boolean(self):
464
 
        """Test BOOL/TINYINT(1) compatibility and reflection."""
465
 
 
466
 
        meta = MetaData(testing.db)
467
 
        bool_table = Table(
468
 
            'mysql_bool',
469
 
            meta,
470
 
            Column('b1', BOOLEAN),
471
 
            Column('b2', Boolean),
472
 
            Column('b3', mysql.MSTinyInteger(1)),
473
 
            Column('b4', mysql.MSTinyInteger(1, unsigned=True)),
474
 
            Column('b5', mysql.MSTinyInteger),
475
 
            )
476
 
        eq_(colspec(bool_table.c.b1), 'b1 BOOL')
477
 
        eq_(colspec(bool_table.c.b2), 'b2 BOOL')
478
 
        eq_(colspec(bool_table.c.b3), 'b3 TINYINT(1)')
479
 
        eq_(colspec(bool_table.c.b4), 'b4 TINYINT(1) UNSIGNED')
480
 
        eq_(colspec(bool_table.c.b5), 'b5 TINYINT')
481
 
        for col in bool_table.c:
482
 
            self.assert_(repr(col))
483
 
        try:
484
 
            meta.create_all()
485
 
            table = bool_table
486
 
 
487
 
            def roundtrip(store, expected=None):
488
 
                expected = expected or store
489
 
                table.insert(store).execute()
490
 
                row = table.select().execute().first()
491
 
                try:
492
 
                    self.assert_(list(row) == expected)
493
 
                    for i, val in enumerate(expected):
494
 
                        if isinstance(val, bool):
495
 
                            self.assert_(val is row[i])
496
 
                except:
497
 
                    print 'Storing %s' % store
498
 
                    print 'Expected %s' % expected
499
 
                    print 'Found %s' % list(row)
500
 
                    raise
501
 
                table.delete().execute().close()
502
 
 
503
 
            roundtrip([None, None, None, None, None])
504
 
            roundtrip([True, True, 1, 1, 1])
505
 
            roundtrip([False, False, 0, 0, 0])
506
 
            roundtrip([True, True, True, True, True], [True, True, 1,
507
 
                      1, 1])
508
 
            roundtrip([False, False, 0, 0, 0], [False, False, 0, 0, 0])
509
 
            meta2 = MetaData(testing.db)
510
 
            table = Table('mysql_bool', meta2, autoload=True)
511
 
            eq_(colspec(table.c.b3), 'b3 TINYINT(1)')
512
 
            eq_(colspec(table.c.b4), 'b4 TINYINT(1) UNSIGNED')
513
 
            meta2 = MetaData(testing.db)
514
 
            table = Table(
515
 
                'mysql_bool',
516
 
                meta2,
517
 
                Column('b1', BOOLEAN),
518
 
                Column('b2', Boolean),
519
 
                Column('b3', BOOLEAN),
520
 
                Column('b4', BOOLEAN),
521
 
                autoload=True,
522
 
                )
523
 
            eq_(colspec(table.c.b3), 'b3 BOOL')
524
 
            eq_(colspec(table.c.b4), 'b4 BOOL')
525
 
            roundtrip([None, None, None, None, None])
526
 
            roundtrip([True, True, 1, 1, 1], [True, True, True, True,
527
 
                      1])
528
 
            roundtrip([False, False, 0, 0, 0], [False, False, False,
529
 
                      False, 0])
530
 
            roundtrip([True, True, True, True, True], [True, True,
531
 
                      True, True, 1])
532
 
            roundtrip([False, False, 0, 0, 0], [False, False, False,
533
 
                      False, 0])
534
 
        finally:
535
 
            meta.drop_all()
536
 
 
537
 
    @testing.exclude('mysql', '<', (4, 1, 0), '4.1+ syntax')
538
 
    def test_timestamp(self):
539
 
        """Exercise funky TIMESTAMP default syntax."""
540
 
 
541
 
        meta = MetaData(testing.db)
542
 
 
543
 
        try:
544
 
            columns = [
545
 
                ([TIMESTAMP],
546
 
                 'TIMESTAMP NULL'),
547
 
                ([mysql.MSTimeStamp],
548
 
                 'TIMESTAMP NULL'),
549
 
                ([mysql.MSTimeStamp,
550
 
                  DefaultClause(sql.text('CURRENT_TIMESTAMP'))],
551
 
                 "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"),
552
 
                ([mysql.MSTimeStamp,
553
 
                  DefaultClause(sql.text("'1999-09-09 09:09:09'"))],
554
 
                 "TIMESTAMP DEFAULT '1999-09-09 09:09:09'"),
555
 
                ([mysql.MSTimeStamp,
556
 
                  DefaultClause(sql.text("'1999-09-09 09:09:09' "
557
 
                                          "ON UPDATE CURRENT_TIMESTAMP"))],
558
 
                 "TIMESTAMP DEFAULT '1999-09-09 09:09:09' "
559
 
                 "ON UPDATE CURRENT_TIMESTAMP"),
560
 
                ([mysql.MSTimeStamp,
561
 
                  DefaultClause(sql.text("CURRENT_TIMESTAMP "
562
 
                                          "ON UPDATE CURRENT_TIMESTAMP"))],
563
 
                 "TIMESTAMP DEFAULT CURRENT_TIMESTAMP "
564
 
                 "ON UPDATE CURRENT_TIMESTAMP"),
565
 
                ]
566
 
            for idx, (spec, expected) in enumerate(columns):
567
 
                t = Table('mysql_ts%s' % idx, meta,
568
 
                          Column('id', Integer, primary_key=True),
569
 
                          Column('t', *spec))
570
 
                eq_(colspec(t.c.t), "t %s" % expected)
571
 
                self.assert_(repr(t.c.t))
572
 
                t.create()
573
 
                r = Table('mysql_ts%s' % idx, MetaData(testing.db),
574
 
                          autoload=True)
575
 
                if len(spec) > 1:
576
 
                    self.assert_(r.c.t is not None)
577
 
        finally:
578
 
            meta.drop_all()
579
 
 
580
 
    def test_timestamp_nullable(self):
581
 
        meta = MetaData(testing.db)
582
 
        ts_table = Table('mysql_timestamp', meta,
583
 
                            Column('t1', TIMESTAMP),
584
 
                            Column('t2', TIMESTAMP, nullable=False),
585
 
                    )
586
 
        meta.create_all()
587
 
        try:
588
 
            # there's a slight assumption here that this test can
589
 
            # complete within the scope of a single second.
590
 
            # if needed, can break out the eq_() just to check for
591
 
            # timestamps that are within a few seconds of "now"
592
 
            # using timedelta.
593
 
 
594
 
            now = testing.db.execute("select now()").scalar()
595
 
 
596
 
            # TIMESTAMP without NULL inserts current time when passed
597
 
            # NULL.  when not passed, generates 0000-00-00 quite
598
 
            # annoyingly.
599
 
            ts_table.insert().execute({'t1':now, 't2':None})
600
 
            ts_table.insert().execute({'t1':None, 't2':None})
601
 
 
602
 
            eq_(
603
 
                ts_table.select().execute().fetchall(),
604
 
                [(now, now), (None, now)]
605
 
            )
606
 
        finally:
607
 
            meta.drop_all()
608
 
 
609
 
    def test_year(self):
610
 
        """Exercise YEAR."""
611
 
 
612
 
        meta = MetaData(testing.db)
613
 
        year_table = Table('mysql_year', meta,
614
 
                           Column('y1', mysql.MSYear),
615
 
                           Column('y2', mysql.MSYear),
616
 
                           Column('y3', mysql.MSYear),
617
 
                           Column('y4', mysql.MSYear(2)),
618
 
                           Column('y5', mysql.MSYear(4)))
619
 
 
620
 
        for col in year_table.c:
621
 
            self.assert_(repr(col))
622
 
        try:
623
 
            year_table.create()
624
 
            reflected = Table('mysql_year', MetaData(testing.db),
625
 
                              autoload=True)
626
 
 
627
 
            for table in year_table, reflected:
628
 
                table.insert(['1950', '50', None, 50, 1950]).execute()
629
 
                row = table.select().execute().first()
630
 
                eq_(list(row), [1950, 2050, None, 50, 1950])
631
 
                table.delete().execute()
632
 
                self.assert_(colspec(table.c.y1).startswith('y1 YEAR'))
633
 
                eq_(colspec(table.c.y4), 'y4 YEAR(2)')
634
 
                eq_(colspec(table.c.y5), 'y5 YEAR(4)')
635
 
        finally:
636
 
            meta.drop_all()
637
 
 
638
 
 
639
 
    def test_set(self):
640
 
        """Exercise the SET type."""
641
 
 
642
 
        meta = MetaData(testing.db)
643
 
        set_table = Table('mysql_set', meta, Column('s1',
644
 
                          mysql.MSSet("'dq'", "'sq'")), Column('s2',
645
 
                          mysql.MSSet("'a'")), Column('s3',
646
 
                          mysql.MSSet("'5'", "'7'", "'9'")))
647
 
        eq_(colspec(set_table.c.s1), "s1 SET('dq','sq')")
648
 
        eq_(colspec(set_table.c.s2), "s2 SET('a')")
649
 
        eq_(colspec(set_table.c.s3), "s3 SET('5','7','9')")
650
 
        for col in set_table.c:
651
 
            self.assert_(repr(col))
652
 
        try:
653
 
            set_table.create()
654
 
            reflected = Table('mysql_set', MetaData(testing.db),
655
 
                              autoload=True)
656
 
            for table in set_table, reflected:
657
 
 
658
 
                def roundtrip(store, expected=None):
659
 
                    expected = expected or store
660
 
                    table.insert(store).execute()
661
 
                    row = table.select().execute().first()
662
 
                    try:
663
 
                        self.assert_(list(row) == expected)
664
 
                    except:
665
 
                        print 'Storing %s' % store
666
 
                        print 'Expected %s' % expected
667
 
                        print 'Found %s' % list(row)
668
 
                        raise
669
 
                    table.delete().execute()
670
 
 
671
 
                roundtrip([None, None, None], [None] * 3)
672
 
                roundtrip(['', '', ''], [set([''])] * 3)
673
 
                roundtrip([set(['dq']), set(['a']), set(['5'])])
674
 
                roundtrip(['dq', 'a', '5'], [set(['dq']), set(['a']),
675
 
                          set(['5'])])
676
 
                roundtrip([1, 1, 1], [set(['dq']), set(['a']), set(['5'
677
 
                          ])])
678
 
                roundtrip([set(['dq', 'sq']), None, set(['9', '5', '7'
679
 
                          ])])
680
 
            set_table.insert().execute({'s3': set(['5'])}, {'s3'
681
 
                    : set(['5', '7'])}, {'s3': set(['5', '7', '9'])},
682
 
                    {'s3': set(['7', '9'])})
683
 
            rows = select([set_table.c.s3], set_table.c.s3.in_([set(['5'
684
 
                          ]), set(['5', '7']), set(['7', '5'
685
 
                          ])])).execute().fetchall()
686
 
            found = set([frozenset(row[0]) for row in rows])
687
 
            eq_(found, set([frozenset(['5']), frozenset(['5', '7'])]))
688
 
        finally:
689
 
            meta.drop_all()
690
 
 
691
 
    @testing.uses_deprecated('Manually quoting ENUM value literals')
692
 
    def test_enum(self):
693
 
        """Exercise the ENUM type."""
694
 
 
695
 
        db = testing.db
696
 
        enum_table = Table('mysql_enum', MetaData(testing.db),
697
 
            Column('e1', mysql.ENUM("'a'", "'b'")),
698
 
            Column('e2', mysql.ENUM("'a'", "'b'"),
699
 
                   nullable=False),
700
 
            Column('e2generic', Enum("a", "b"),
701
 
                  nullable=False),
702
 
            Column('e3', mysql.ENUM("'a'", "'b'", strict=True)),
703
 
            Column('e4', mysql.ENUM("'a'", "'b'", strict=True),
704
 
                   nullable=False),
705
 
            Column('e5', mysql.ENUM("a", "b")),
706
 
            Column('e5generic', Enum("a", "b")),
707
 
            Column('e6', mysql.ENUM("'a'", "b")),
708
 
            )
709
 
 
710
 
        eq_(colspec(enum_table.c.e1),
711
 
                       "e1 ENUM('a','b')")
712
 
        eq_(colspec(enum_table.c.e2),
713
 
                       "e2 ENUM('a','b') NOT NULL")
714
 
        eq_(colspec(enum_table.c.e2generic),
715
 
                      "e2generic ENUM('a','b') NOT NULL")
716
 
        eq_(colspec(enum_table.c.e3),
717
 
                       "e3 ENUM('a','b')")
718
 
        eq_(colspec(enum_table.c.e4),
719
 
                       "e4 ENUM('a','b') NOT NULL")
720
 
        eq_(colspec(enum_table.c.e5),
721
 
                       "e5 ENUM('a','b')")
722
 
        eq_(colspec(enum_table.c.e5generic),
723
 
                      "e5generic ENUM('a','b')")
724
 
        eq_(colspec(enum_table.c.e6),
725
 
                       "e6 ENUM('''a''','b')")
726
 
        enum_table.drop(checkfirst=True)
727
 
        enum_table.create()
728
 
 
729
 
        assert_raises(exc.DBAPIError, enum_table.insert().execute,
730
 
                        e1=None, e2=None, e3=None, e4=None)
731
 
 
732
 
        assert_raises(exc.StatementError, enum_table.insert().execute,
733
 
                                        e1='c', e2='c', e2generic='c', e3='c',
734
 
                                        e4='c', e5='c', e5generic='c', e6='c')
735
 
 
736
 
        enum_table.insert().execute()
737
 
        enum_table.insert().execute(e1='a', e2='a', e2generic='a', e3='a',
738
 
                                    e4='a', e5='a', e5generic='a', e6="'a'")
739
 
        enum_table.insert().execute(e1='b', e2='b', e2generic='b', e3='b',
740
 
                                    e4='b', e5='b', e5generic='b', e6='b')
741
 
 
742
 
        res = enum_table.select().execute().fetchall()
743
 
 
744
 
        expected = [(None, 'a', 'a', None, 'a', None, None, None),
745
 
                    ('a', 'a', 'a', 'a', 'a', 'a', 'a', "'a'"),
746
 
                    ('b', 'b', 'b', 'b', 'b', 'b', 'b', 'b')]
747
 
 
748
 
        # This is known to fail with MySQLDB 1.2.2 beta versions
749
 
        # which return these as sets.Set(['a']), sets.Set(['b'])
750
 
        # (even on Pythons with __builtin__.set)
751
 
        if (testing.against('mysql+mysqldb') and
752
 
            testing.db.dialect.dbapi.version_info < (1, 2, 2, 'beta', 3) and
753
 
            testing.db.dialect.dbapi.version_info >= (1, 2, 2)):
754
 
            # these mysqldb seem to always uses 'sets', even on later pythons
755
 
            import sets
756
 
            def convert(value):
757
 
                if value is None:
758
 
                    return value
759
 
                if value == '':
760
 
                    return sets.Set([])
761
 
                else:
762
 
                    return sets.Set([value])
763
 
 
764
 
            e = []
765
 
            for row in expected:
766
 
                e.append(tuple([convert(c) for c in row]))
767
 
            expected = e
768
 
 
769
 
        eq_(res, expected)
770
 
        enum_table.drop()
771
 
 
772
 
    def test_unicode_enum(self):
773
 
        unicode_engine = utf8_engine()
774
 
        metadata = MetaData(unicode_engine)
775
 
        t1 = Table('table', metadata,
776
 
            Column('id', Integer, primary_key=True),
777
 
            Column('value', Enum(u'réveillé', u'drôle', u'S’il')),
778
 
            Column('value2', mysql.ENUM(u'réveillé', u'drôle', u'S’il'))
779
 
        )
780
 
        metadata.create_all()
781
 
        try:
782
 
            t1.insert().execute(value=u'drôle', value2=u'drôle')
783
 
            t1.insert().execute(value=u'réveillé', value2=u'réveillé')
784
 
            t1.insert().execute(value=u'S’il', value2=u'S’il')
785
 
            eq_(t1.select().order_by(t1.c.id).execute().fetchall(),
786
 
                [(1, u'drôle', u'drôle'), (2, u'réveillé', u'réveillé'),
787
 
                            (3, u'S’il', u'S’il')]
788
 
            )
789
 
 
790
 
            # test reflection of the enum labels
791
 
 
792
 
            m2 = MetaData(testing.db)
793
 
            t2 = Table('table', m2, autoload=True)
794
 
 
795
 
            # TODO: what's wrong with the last element ?  is there
796
 
            # latin-1 stuff forcing its way in ?
797
 
 
798
 
            assert t2.c.value.type.enums[0:2] == \
799
 
                    (u'réveillé', u'drôle') #, u'S’il') # eh ?
800
 
            assert t2.c.value2.type.enums[0:2] == \
801
 
                    (u'réveillé', u'drôle') #, u'S’il') # eh ?
802
 
        finally:
803
 
            metadata.drop_all()
804
 
 
805
 
    def test_enum_compile(self):
806
 
        e1 = Enum('x', 'y', 'z', name='somename')
807
 
        t1 = Table('sometable', MetaData(), Column('somecolumn', e1))
808
 
        self.assert_compile(schema.CreateTable(t1),
809
 
                            "CREATE TABLE sometable (somecolumn "
810
 
                            "ENUM('x','y','z'))")
811
 
        t1 = Table('sometable', MetaData(), Column('somecolumn',
812
 
                   Enum('x', 'y', 'z', native_enum=False)))
813
 
        self.assert_compile(schema.CreateTable(t1),
814
 
                            "CREATE TABLE sometable (somecolumn "
815
 
                            "VARCHAR(1), CHECK (somecolumn IN ('x', "
816
 
                            "'y', 'z')))")
817
 
 
818
 
    @testing.exclude('mysql', '<', (4,), "3.23 can't handle an ENUM of ''")
819
 
    @testing.uses_deprecated('Manually quoting ENUM value literals')
820
 
    def test_enum_parse(self):
821
 
        """More exercises for the ENUM type."""
822
 
 
823
 
        # MySQL 3.23 can't handle an ENUM of ''....
824
 
 
825
 
        enum_table = Table('mysql_enum', MetaData(testing.db),
826
 
            Column('e1', mysql.ENUM("'a'")),
827
 
            Column('e2', mysql.ENUM("''")),
828
 
            Column('e3', mysql.ENUM('a')),
829
 
            Column('e4', mysql.ENUM('')),
830
 
            Column('e5', mysql.ENUM("'a'", "''")),
831
 
            Column('e6', mysql.ENUM("''", "'a'")),
832
 
            Column('e7', mysql.ENUM("''", "'''a'''", "'b''b'", "''''")))
833
 
 
834
 
        for col in enum_table.c:
835
 
            self.assert_(repr(col))
836
 
        try:
837
 
            enum_table.create()
838
 
            reflected = Table('mysql_enum', MetaData(testing.db),
839
 
                              autoload=True)
840
 
            for t in enum_table, reflected:
841
 
                eq_(t.c.e1.type.enums, ("a",))
842
 
                eq_(t.c.e2.type.enums, ("",))
843
 
                eq_(t.c.e3.type.enums, ("a",))
844
 
                eq_(t.c.e4.type.enums, ("",))
845
 
                eq_(t.c.e5.type.enums, ("a", ""))
846
 
                eq_(t.c.e6.type.enums, ("", "a"))
847
 
                eq_(t.c.e7.type.enums, ("", "'a'", "b'b", "'"))
848
 
        finally:
849
 
            enum_table.drop()
850
 
 
851
 
class ReflectionTest(fixtures.TestBase, AssertsExecutionResults):
852
 
 
853
 
    __only_on__ = 'mysql'
854
 
 
855
 
    def test_default_reflection(self):
856
 
        """Test reflection of column defaults."""
857
 
 
858
 
        from sqlalchemy.dialects.mysql import VARCHAR
859
 
        def_table = Table(
860
 
            'mysql_def',
861
 
            MetaData(testing.db),
862
 
            Column('c1', VARCHAR(10, collation='utf8_unicode_ci'),
863
 
                   DefaultClause(''), nullable=False),
864
 
            Column('c2', String(10), DefaultClause('0')),
865
 
            Column('c3', String(10), DefaultClause('abc')),
866
 
            Column('c4', TIMESTAMP, DefaultClause('2009-04-05 12:00:00'
867
 
                   )),
868
 
            Column('c5', TIMESTAMP),
869
 
            Column('c6', TIMESTAMP,
870
 
                   DefaultClause(sql.text("CURRENT_TIMESTAMP "
871
 
                                          "ON UPDATE CURRENT_TIMESTAMP"))),
872
 
            )
873
 
        def_table.create()
874
 
        try:
875
 
            reflected = Table('mysql_def', MetaData(testing.db),
876
 
                              autoload=True)
877
 
        finally:
878
 
            def_table.drop()
879
 
        assert def_table.c.c1.server_default.arg == ''
880
 
        assert def_table.c.c2.server_default.arg == '0'
881
 
        assert def_table.c.c3.server_default.arg == 'abc'
882
 
        assert def_table.c.c4.server_default.arg \
883
 
            == '2009-04-05 12:00:00'
884
 
        assert str(reflected.c.c1.server_default.arg) == "''"
885
 
        assert str(reflected.c.c2.server_default.arg) == "'0'"
886
 
        assert str(reflected.c.c3.server_default.arg) == "'abc'"
887
 
        assert str(reflected.c.c4.server_default.arg) \
888
 
            == "'2009-04-05 12:00:00'"
889
 
        assert reflected.c.c5.default is None
890
 
        assert reflected.c.c5.server_default is None
891
 
        assert reflected.c.c6.default is None
892
 
        eq_(
893
 
            str(reflected.c.c6.server_default.arg).upper(),
894
 
            "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
895
 
        )
896
 
        reflected.create()
897
 
        try:
898
 
            reflected2 = Table('mysql_def', MetaData(testing.db),
899
 
                               autoload=True)
900
 
        finally:
901
 
            reflected.drop()
902
 
        assert str(reflected2.c.c1.server_default.arg) == "''"
903
 
        assert str(reflected2.c.c2.server_default.arg) == "'0'"
904
 
        assert str(reflected2.c.c3.server_default.arg) == "'abc'"
905
 
        assert str(reflected2.c.c4.server_default.arg) \
906
 
            == "'2009-04-05 12:00:00'"
907
 
        assert reflected.c.c5.default is None
908
 
        assert reflected.c.c5.server_default is None
909
 
        assert reflected.c.c6.default is None
910
 
        eq_(
911
 
            str(reflected.c.c6.server_default.arg).upper(),
912
 
            "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
913
 
        )
914
 
 
915
 
    def test_reflection_with_table_options(self):
916
 
        comment = r"""Comment types type speedily ' " \ '' Fun!"""
917
 
 
918
 
        def_table = Table('mysql_def', MetaData(testing.db),
919
 
            Column('c1', Integer()),
920
 
            mysql_engine='MEMORY',
921
 
            mysql_comment=comment,
922
 
            mysql_default_charset='utf8',
923
 
            mysql_auto_increment='5',
924
 
            mysql_avg_row_length='3',
925
 
            mysql_password='secret',
926
 
            mysql_connection='fish',
927
 
        )
928
 
 
929
 
        def_table.create()
930
 
        try:
931
 
            reflected = Table('mysql_def', MetaData(testing.db),
932
 
                          autoload=True)
933
 
        finally:
934
 
            def_table.drop()
935
 
 
936
 
        assert def_table.kwargs['mysql_engine'] == 'MEMORY'
937
 
        assert def_table.kwargs['mysql_comment'] == comment
938
 
        assert def_table.kwargs['mysql_default_charset'] == 'utf8'
939
 
        assert def_table.kwargs['mysql_auto_increment'] == '5'
940
 
        assert def_table.kwargs['mysql_avg_row_length'] == '3'
941
 
        assert def_table.kwargs['mysql_password'] == 'secret'
942
 
        assert def_table.kwargs['mysql_connection'] == 'fish'
943
 
 
944
 
        assert reflected.kwargs['mysql_engine'] == 'MEMORY'
945
 
        assert reflected.kwargs['mysql_comment'] == comment
946
 
        assert reflected.kwargs['mysql_default charset'] == 'utf8'
947
 
        assert reflected.kwargs['mysql_avg_row_length'] == '3'
948
 
        assert reflected.kwargs['mysql_connection'] == 'fish'
949
 
 
950
 
        # This field doesn't seem to be returned by mysql itself.
951
 
        #assert reflected.kwargs['mysql_password'] == 'secret'
952
 
 
953
 
        # This is explicitly ignored when reflecting schema.
954
 
        #assert reflected.kwargs['mysql_auto_increment'] == '5'
955
 
 
956
 
    def test_reflection_on_include_columns(self):
957
 
        """Test reflection of include_columns to be sure they respect case."""
958
 
 
959
 
        case_table = Table('mysql_case', MetaData(testing.db),
960
 
            Column('c1', String(10)),
961
 
            Column('C2', String(10)),
962
 
            Column('C3', String(10)))
963
 
 
964
 
        try:
965
 
            case_table.create()
966
 
            reflected = Table('mysql_case', MetaData(testing.db),
967
 
                              autoload=True, include_columns=['c1', 'C2'])
968
 
            for t in case_table, reflected:
969
 
                assert 'c1' in t.c.keys()
970
 
                assert 'C2' in t.c.keys()
971
 
            reflected2 = Table('mysql_case', MetaData(testing.db),
972
 
                              autoload=True, include_columns=['c1', 'c2'])
973
 
            assert 'c1' in reflected2.c.keys()
974
 
            for c in ['c2', 'C2', 'C3']:
975
 
                assert c not in reflected2.c.keys()
976
 
        finally:
977
 
            case_table.drop()
978
 
 
979
 
    @testing.exclude('mysql', '<', (5, 0, 0), 'early types are squirrely')
980
 
    @testing.uses_deprecated('Using String type with no length')
981
 
    @testing.uses_deprecated('Manually quoting ENUM value literals')
982
 
    def test_type_reflection(self):
983
 
        # (ask_for, roundtripped_as_if_different)
984
 
        specs = [( String(1), mysql.MSString(1), ),
985
 
                 ( String(3), mysql.MSString(3), ),
986
 
                 ( Text(), mysql.MSText(), ),
987
 
                 ( Unicode(1), mysql.MSString(1), ),
988
 
                 ( Unicode(3), mysql.MSString(3), ),
989
 
                 ( UnicodeText(), mysql.MSText(), ),
990
 
                 ( mysql.MSChar(1), ),
991
 
                 ( mysql.MSChar(3), ),
992
 
                 ( NCHAR(2), mysql.MSChar(2), ),
993
 
                 ( mysql.MSNChar(2), mysql.MSChar(2), ), # N is CREATE only
994
 
                 ( mysql.MSNVarChar(22), mysql.MSString(22), ),
995
 
                 ( SmallInteger(), mysql.MSSmallInteger(), ),
996
 
                 ( SmallInteger(), mysql.MSSmallInteger(4), ),
997
 
                 ( mysql.MSSmallInteger(), ),
998
 
                 ( mysql.MSSmallInteger(4), mysql.MSSmallInteger(4), ),
999
 
                 ( mysql.MSMediumInteger(), mysql.MSMediumInteger(), ),
1000
 
                 ( mysql.MSMediumInteger(8), mysql.MSMediumInteger(8), ),
1001
 
                 ( LargeBinary(3), mysql.TINYBLOB(), ),
1002
 
                 ( LargeBinary(), mysql.BLOB() ),
1003
 
                 ( mysql.MSBinary(3), mysql.MSBinary(3), ),
1004
 
                 ( mysql.MSVarBinary(3),),
1005
 
                 ( mysql.MSTinyBlob(),),
1006
 
                 ( mysql.MSBlob(),),
1007
 
                 ( mysql.MSBlob(1234), mysql.MSBlob()),
1008
 
                 ( mysql.MSMediumBlob(),),
1009
 
                 ( mysql.MSLongBlob(),),
1010
 
                 ( mysql.ENUM("''","'fleem'"), ),
1011
 
                 ]
1012
 
 
1013
 
        columns = [Column('c%i' % (i + 1), t[0]) for i, t in enumerate(specs)]
1014
 
 
1015
 
        db = testing.db
1016
 
        m = MetaData(db)
1017
 
        t_table = Table('mysql_types', m, *columns)
1018
 
        try:
1019
 
            m.create_all()
1020
 
 
1021
 
            m2 = MetaData(db)
1022
 
            rt = Table('mysql_types', m2, autoload=True)
1023
 
            try:
1024
 
                db.execute('CREATE OR REPLACE VIEW mysql_types_v '
1025
 
                           'AS SELECT * from mysql_types')
1026
 
                rv = Table('mysql_types_v', m2, autoload=True)
1027
 
 
1028
 
                expected = [len(c) > 1 and c[1] or c[0] for c in specs]
1029
 
 
1030
 
                # Early 5.0 releases seem to report more "general" for columns
1031
 
                # in a view, e.g. char -> varchar, tinyblob -> mediumblob
1032
 
                #
1033
 
                # Not sure exactly which point version has the fix.
1034
 
                if db.dialect.server_version_info < (5, 0, 11):
1035
 
                    tables = rt,
1036
 
                else:
1037
 
                    tables = rt, rv
1038
 
 
1039
 
                for table in tables:
1040
 
                    for i, reflected in enumerate(table.c):
1041
 
                        assert isinstance(reflected.type,
1042
 
                                type(expected[i])), \
1043
 
                            'element %d: %r not instance of %r' % (i,
1044
 
                                reflected.type, type(expected[i]))
1045
 
            finally:
1046
 
                db.execute('DROP VIEW mysql_types_v')
1047
 
        finally:
1048
 
            m.drop_all()
1049
 
 
1050
 
    def test_autoincrement(self):
1051
 
        meta = MetaData(testing.db)
1052
 
        try:
1053
 
            Table('ai_1', meta,
1054
 
                  Column('int_y', Integer, primary_key=True),
1055
 
                  Column('int_n', Integer, DefaultClause('0'),
1056
 
                         primary_key=True),
1057
 
                         mysql_engine='MyISAM')
1058
 
            Table('ai_2', meta,
1059
 
                  Column('int_y', Integer, primary_key=True),
1060
 
                  Column('int_n', Integer, DefaultClause('0'),
1061
 
                         primary_key=True),
1062
 
                         mysql_engine='MyISAM')
1063
 
            Table('ai_3', meta,
1064
 
                  Column('int_n', Integer, DefaultClause('0'),
1065
 
                         primary_key=True, autoincrement=False),
1066
 
                  Column('int_y', Integer, primary_key=True),
1067
 
                         mysql_engine='MyISAM')
1068
 
            Table('ai_4', meta,
1069
 
                  Column('int_n', Integer, DefaultClause('0'),
1070
 
                         primary_key=True, autoincrement=False),
1071
 
                  Column('int_n2', Integer, DefaultClause('0'),
1072
 
                         primary_key=True, autoincrement=False),
1073
 
                         mysql_engine='MyISAM')
1074
 
            Table('ai_5', meta,
1075
 
                  Column('int_y', Integer, primary_key=True),
1076
 
                  Column('int_n', Integer, DefaultClause('0'),
1077
 
                         primary_key=True, autoincrement=False),
1078
 
                         mysql_engine='MyISAM')
1079
 
            Table('ai_6', meta,
1080
 
                  Column('o1', String(1), DefaultClause('x'),
1081
 
                         primary_key=True),
1082
 
                  Column('int_y', Integer, primary_key=True),
1083
 
                         mysql_engine='MyISAM')
1084
 
            Table('ai_7', meta,
1085
 
                  Column('o1', String(1), DefaultClause('x'),
1086
 
                         primary_key=True),
1087
 
                  Column('o2', String(1), DefaultClause('x'),
1088
 
                         primary_key=True),
1089
 
                  Column('int_y', Integer, primary_key=True),
1090
 
                         mysql_engine='MyISAM')
1091
 
            Table('ai_8', meta,
1092
 
                  Column('o1', String(1), DefaultClause('x'),
1093
 
                         primary_key=True),
1094
 
                  Column('o2', String(1), DefaultClause('x'),
1095
 
                         primary_key=True),
1096
 
                         mysql_engine='MyISAM')
1097
 
            meta.create_all()
1098
 
 
1099
 
            table_names = ['ai_1', 'ai_2', 'ai_3', 'ai_4',
1100
 
                           'ai_5', 'ai_6', 'ai_7', 'ai_8']
1101
 
            mr = MetaData(testing.db)
1102
 
            mr.reflect(only=table_names)
1103
 
 
1104
 
            for tbl in [mr.tables[name] for name in table_names]:
1105
 
                for c in tbl.c:
1106
 
                    if c.name.startswith('int_y'):
1107
 
                        assert c.autoincrement
1108
 
                    elif c.name.startswith('int_n'):
1109
 
                        assert not c.autoincrement
1110
 
                tbl.insert().execute()
1111
 
                if 'int_y' in tbl.c:
1112
 
                    assert select([tbl.c.int_y]).scalar() == 1
1113
 
                    assert list(tbl.select().execute().first()).count(1) == 1
1114
 
                else:
1115
 
                    assert 1 not in list(tbl.select().execute().first())
1116
 
        finally:
1117
 
            meta.drop_all()
1118
 
 
1119
 
    @testing.exclude('mysql', '<', (5, 0, 0), 'no information_schema support')
1120
 
    def test_system_views(self):
1121
 
        dialect = testing.db.dialect
1122
 
        connection = testing.db.connect()
1123
 
        view_names = dialect.get_view_names(connection, "information_schema")
1124
 
        self.assert_('TABLES' in view_names)
1125
 
 
1126
 
 
1127
 
class SQLTest(fixtures.TestBase, AssertsCompiledSQL):
1128
 
    """Tests MySQL-dialect specific compilation."""
1129
 
 
1130
 
    __dialect__ = mysql.dialect()
1131
 
 
1132
 
    def test_precolumns(self):
1133
 
        dialect = self.__dialect__
1134
 
 
1135
 
        def gen(distinct=None, prefixes=None):
1136
 
            kw = {}
1137
 
            if distinct is not None:
1138
 
                kw['distinct'] = distinct
1139
 
            if prefixes is not None:
1140
 
                kw['prefixes'] = prefixes
1141
 
            return str(select(['q'], **kw).compile(dialect=dialect))
1142
 
 
1143
 
        eq_(gen(None), 'SELECT q')
1144
 
        eq_(gen(True), 'SELECT DISTINCT q')
1145
 
 
1146
 
        assert_raises(
1147
 
            exc.SADeprecationWarning,
1148
 
            gen, 'DISTINCT'
1149
 
        )
1150
 
 
1151
 
        eq_(gen(prefixes=['ALL']), 'SELECT ALL q')
1152
 
        eq_(gen(prefixes=['DISTINCTROW']),
1153
 
                'SELECT DISTINCTROW q')
1154
 
 
1155
 
        # Interaction with MySQL prefix extensions
1156
 
        eq_(
1157
 
            gen(None, ['straight_join']),
1158
 
            'SELECT straight_join q')
1159
 
        eq_(
1160
 
            gen(False, ['HIGH_PRIORITY', 'SQL_SMALL_RESULT', 'ALL']),
1161
 
            'SELECT HIGH_PRIORITY SQL_SMALL_RESULT ALL q')
1162
 
        eq_(
1163
 
            gen(True, ['high_priority', sql.text('sql_cache')]),
1164
 
            'SELECT high_priority sql_cache DISTINCT q')
1165
 
 
1166
 
    @testing.uses_deprecated
1167
 
    def test_deprecated_distinct(self):
1168
 
        dialect = self.__dialect__
1169
 
 
1170
 
        self.assert_compile(
1171
 
            select(['q'], distinct='ALL'),
1172
 
            'SELECT ALL q',
1173
 
        )
1174
 
 
1175
 
        self.assert_compile(
1176
 
            select(['q'], distinct='distinctROW'),
1177
 
            'SELECT DISTINCTROW q',
1178
 
        )
1179
 
 
1180
 
        self.assert_compile(
1181
 
            select(['q'], distinct='ALL',
1182
 
                    prefixes=['HIGH_PRIORITY', 'SQL_SMALL_RESULT']),
1183
 
            'SELECT HIGH_PRIORITY SQL_SMALL_RESULT ALL q'
1184
 
        )
1185
 
 
1186
 
    def test_backslash_escaping(self):
1187
 
        self.assert_compile(
1188
 
            sql.column('foo').like('bar', escape='\\'),
1189
 
            "foo LIKE %s ESCAPE '\\\\'"
1190
 
        )
1191
 
 
1192
 
        dialect = mysql.dialect()
1193
 
        dialect._backslash_escapes=False
1194
 
        self.assert_compile(
1195
 
            sql.column('foo').like('bar', escape='\\'),
1196
 
            "foo LIKE %s ESCAPE '\\'",
1197
 
            dialect=dialect
1198
 
        )
1199
 
 
1200
 
    def test_limit(self):
1201
 
        t = sql.table('t', sql.column('col1'), sql.column('col2'))
1202
 
 
1203
 
        self.assert_compile(
1204
 
            select([t]).limit(10).offset(20),
1205
 
            "SELECT t.col1, t.col2 FROM t  LIMIT %s, %s",
1206
 
            {'param_1':20, 'param_2':10}
1207
 
            )
1208
 
        self.assert_compile(
1209
 
            select([t]).limit(10),
1210
 
            "SELECT t.col1, t.col2 FROM t  LIMIT %s",
1211
 
            {'param_1':10})
1212
 
 
1213
 
        self.assert_compile(
1214
 
            select([t]).offset(10),
1215
 
            "SELECT t.col1, t.col2 FROM t  LIMIT %s, 18446744073709551615",
1216
 
            {'param_1':10}
1217
 
            )
1218
 
 
1219
 
    def test_varchar_raise(self):
1220
 
        for type_ in (
1221
 
            String,
1222
 
            VARCHAR,
1223
 
            String(),
1224
 
            VARCHAR(),
1225
 
            NVARCHAR(),
1226
 
            Unicode,
1227
 
            Unicode(),
1228
 
        ):
1229
 
            type_ = sqltypes.to_instance(type_)
1230
 
            assert_raises_message(
1231
 
                exc.CompileError,
1232
 
                "VARCHAR requires a length on dialect mysql",
1233
 
                type_.compile,
1234
 
            dialect=mysql.dialect())
1235
 
 
1236
 
            t1 = Table('sometable', MetaData(),
1237
 
                Column('somecolumn', type_)
1238
 
            )
1239
 
            assert_raises_message(
1240
 
                exc.CompileError,
1241
 
                r"\(in table 'sometable', column 'somecolumn'\)\: "
1242
 
                r"(?:N)?VARCHAR requires a length on dialect mysql",
1243
 
                schema.CreateTable(t1).compile,
1244
 
                dialect=mysql.dialect()
1245
 
            )
1246
 
 
1247
 
    def test_update_limit(self):
1248
 
        t = sql.table('t', sql.column('col1'), sql.column('col2'))
1249
 
 
1250
 
        self.assert_compile(
1251
 
            t.update(values={'col1':123}),
1252
 
            "UPDATE t SET col1=%s"
1253
 
            )
1254
 
        self.assert_compile(
1255
 
            t.update(values={'col1':123}, mysql_limit=5),
1256
 
            "UPDATE t SET col1=%s LIMIT 5"
1257
 
            )
1258
 
        self.assert_compile(
1259
 
            t.update(values={'col1':123}, mysql_limit=None),
1260
 
            "UPDATE t SET col1=%s"
1261
 
            )
1262
 
        self.assert_compile(
1263
 
            t.update(t.c.col2==456, values={'col1':123}, mysql_limit=1),
1264
 
            "UPDATE t SET col1=%s WHERE t.col2 = %s LIMIT 1"
1265
 
            )
1266
 
 
1267
 
    def test_utc_timestamp(self):
1268
 
        self.assert_compile(func.utc_timestamp(), "UTC_TIMESTAMP")
1269
 
 
1270
 
    def test_sysdate(self):
1271
 
        self.assert_compile(func.sysdate(), "SYSDATE()")
1272
 
 
1273
 
    def test_cast(self):
1274
 
        t = sql.table('t', sql.column('col'))
1275
 
        m = mysql
1276
 
 
1277
 
        specs = [
1278
 
            (Integer, "CAST(t.col AS SIGNED INTEGER)"),
1279
 
            (INT, "CAST(t.col AS SIGNED INTEGER)"),
1280
 
            (m.MSInteger, "CAST(t.col AS SIGNED INTEGER)"),
1281
 
            (m.MSInteger(unsigned=True), "CAST(t.col AS UNSIGNED INTEGER)"),
1282
 
            (SmallInteger, "CAST(t.col AS SIGNED INTEGER)"),
1283
 
            (m.MSSmallInteger, "CAST(t.col AS SIGNED INTEGER)"),
1284
 
            (m.MSTinyInteger, "CAST(t.col AS SIGNED INTEGER)"),
1285
 
            # 'SIGNED INTEGER' is a bigint, so this is ok.
1286
 
            (m.MSBigInteger, "CAST(t.col AS SIGNED INTEGER)"),
1287
 
            (m.MSBigInteger(unsigned=False), "CAST(t.col AS SIGNED INTEGER)"),
1288
 
            (m.MSBigInteger(unsigned=True),
1289
 
                            "CAST(t.col AS UNSIGNED INTEGER)"),
1290
 
            (m.MSBit, "t.col"),
1291
 
 
1292
 
            # this is kind of sucky.  thank you default arguments!
1293
 
            (NUMERIC, "CAST(t.col AS DECIMAL)"),
1294
 
            (DECIMAL, "CAST(t.col AS DECIMAL)"),
1295
 
            (Numeric, "CAST(t.col AS DECIMAL)"),
1296
 
            (m.MSNumeric, "CAST(t.col AS DECIMAL)"),
1297
 
            (m.MSDecimal, "CAST(t.col AS DECIMAL)"),
1298
 
 
1299
 
            (FLOAT, "t.col"),
1300
 
            (Float, "t.col"),
1301
 
            (m.MSFloat, "t.col"),
1302
 
            (m.MSDouble, "t.col"),
1303
 
            (m.MSReal, "t.col"),
1304
 
 
1305
 
            (TIMESTAMP, "CAST(t.col AS DATETIME)"),
1306
 
            (DATETIME, "CAST(t.col AS DATETIME)"),
1307
 
            (DATE, "CAST(t.col AS DATE)"),
1308
 
            (TIME, "CAST(t.col AS TIME)"),
1309
 
            (DateTime, "CAST(t.col AS DATETIME)"),
1310
 
            (Date, "CAST(t.col AS DATE)"),
1311
 
            (Time, "CAST(t.col AS TIME)"),
1312
 
            (DateTime, "CAST(t.col AS DATETIME)"),
1313
 
            (Date, "CAST(t.col AS DATE)"),
1314
 
            (m.MSTime, "CAST(t.col AS TIME)"),
1315
 
            (m.MSTimeStamp, "CAST(t.col AS DATETIME)"),
1316
 
            (m.MSYear, "t.col"),
1317
 
            (m.MSYear(2), "t.col"),
1318
 
            (Interval, "t.col"),
1319
 
 
1320
 
            (String, "CAST(t.col AS CHAR)"),
1321
 
            (Unicode, "CAST(t.col AS CHAR)"),
1322
 
            (UnicodeText, "CAST(t.col AS CHAR)"),
1323
 
            (VARCHAR, "CAST(t.col AS CHAR)"),
1324
 
            (NCHAR, "CAST(t.col AS CHAR)"),
1325
 
            (CHAR, "CAST(t.col AS CHAR)"),
1326
 
            (CLOB, "CAST(t.col AS CHAR)"),
1327
 
            (TEXT, "CAST(t.col AS CHAR)"),
1328
 
            (String(32), "CAST(t.col AS CHAR(32))"),
1329
 
            (Unicode(32), "CAST(t.col AS CHAR(32))"),
1330
 
            (CHAR(32), "CAST(t.col AS CHAR(32))"),
1331
 
            (m.MSString, "CAST(t.col AS CHAR)"),
1332
 
            (m.MSText, "CAST(t.col AS CHAR)"),
1333
 
            (m.MSTinyText, "CAST(t.col AS CHAR)"),
1334
 
            (m.MSMediumText, "CAST(t.col AS CHAR)"),
1335
 
            (m.MSLongText, "CAST(t.col AS CHAR)"),
1336
 
            (m.MSNChar, "CAST(t.col AS CHAR)"),
1337
 
            (m.MSNVarChar, "CAST(t.col AS CHAR)"),
1338
 
 
1339
 
            (LargeBinary, "CAST(t.col AS BINARY)"),
1340
 
            (BLOB, "CAST(t.col AS BINARY)"),
1341
 
            (m.MSBlob, "CAST(t.col AS BINARY)"),
1342
 
            (m.MSBlob(32), "CAST(t.col AS BINARY)"),
1343
 
            (m.MSTinyBlob, "CAST(t.col AS BINARY)"),
1344
 
            (m.MSMediumBlob, "CAST(t.col AS BINARY)"),
1345
 
            (m.MSLongBlob, "CAST(t.col AS BINARY)"),
1346
 
            (m.MSBinary, "CAST(t.col AS BINARY)"),
1347
 
            (m.MSBinary(32), "CAST(t.col AS BINARY)"),
1348
 
            (m.MSVarBinary, "CAST(t.col AS BINARY)"),
1349
 
            (m.MSVarBinary(32), "CAST(t.col AS BINARY)"),
1350
 
 
1351
 
            # maybe this could be changed to something more DWIM, needs
1352
 
            # testing
1353
 
            (Boolean, "t.col"),
1354
 
            (BOOLEAN, "t.col"),
1355
 
 
1356
 
            (m.MSEnum, "t.col"),
1357
 
            (m.MSEnum("1", "2"), "t.col"),
1358
 
            (m.MSSet, "t.col"),
1359
 
            (m.MSSet("1", "2"), "t.col"),
1360
 
            ]
1361
 
 
1362
 
        for type_, expected in specs:
1363
 
            self.assert_compile(cast(t.c.col, type_), expected)
1364
 
 
1365
 
    def test_no_cast_pre_4(self):
1366
 
        self.assert_compile(
1367
 
                    cast(Column('foo', Integer), String),
1368
 
                    "CAST(foo AS CHAR)",
1369
 
            )
1370
 
        dialect = mysql.dialect()
1371
 
        dialect.server_version_info = (3, 2, 3)
1372
 
        self.assert_compile(
1373
 
                    cast(Column('foo', Integer), String),
1374
 
                    "foo",
1375
 
                    dialect=dialect
1376
 
            )
1377
 
 
1378
 
    def test_cast_grouped_expression_non_castable(self):
1379
 
        self.assert_compile(
1380
 
            cast(sql.column('x') + sql.column('y'), Float),
1381
 
            "(x + y)"
1382
 
        )
1383
 
 
1384
 
    def test_cast_grouped_expression_pre_4(self):
1385
 
        dialect = mysql.dialect()
1386
 
        dialect.server_version_info = (3, 2, 3)
1387
 
        self.assert_compile(
1388
 
            cast(sql.column('x') + sql.column('y'), Integer),
1389
 
            "(x + y)",
1390
 
            dialect=dialect
1391
 
        )
1392
 
 
1393
 
    def test_extract(self):
1394
 
        t = sql.table('t', sql.column('col1'))
1395
 
 
1396
 
        for field in 'year', 'month', 'day':
1397
 
            self.assert_compile(
1398
 
                select([extract(field, t.c.col1)]),
1399
 
                "SELECT EXTRACT(%s FROM t.col1) AS anon_1 FROM t" % field)
1400
 
 
1401
 
        # millsecondS to millisecond
1402
 
        self.assert_compile(
1403
 
            select([extract('milliseconds', t.c.col1)]),
1404
 
            "SELECT EXTRACT(millisecond FROM t.col1) AS anon_1 FROM t")
1405
 
 
1406
 
    def test_too_long_index(self):
1407
 
        exp = 'ix_zyrenian_zyme_zyzzogeton_zyzzogeton_zyrenian_zyme_zyz_5cd2'
1408
 
        tname = 'zyrenian_zyme_zyzzogeton_zyzzogeton'
1409
 
        cname = 'zyrenian_zyme_zyzzogeton_zo'
1410
 
 
1411
 
        t1 = Table(tname, MetaData(),
1412
 
                    Column(cname, Integer, index=True),
1413
 
                )
1414
 
        ix1 = list(t1.indexes)[0]
1415
 
 
1416
 
        self.assert_compile(
1417
 
            schema.CreateIndex(ix1),
1418
 
            "CREATE INDEX %s "
1419
 
            "ON %s (%s)" % (exp, tname, cname),
1420
 
            dialect=mysql.dialect()
1421
 
        )
1422
 
 
1423
 
    def test_innodb_autoincrement(self):
1424
 
        t1 = Table('sometable', MetaData(), Column('assigned_id',
1425
 
                   Integer(), primary_key=True, autoincrement=False),
1426
 
                   Column('id', Integer(), primary_key=True,
1427
 
                   autoincrement=True), mysql_engine='InnoDB')
1428
 
        self.assert_compile(schema.CreateTable(t1),
1429
 
                            'CREATE TABLE sometable (assigned_id '
1430
 
                            'INTEGER NOT NULL, id INTEGER NOT NULL '
1431
 
                            'AUTO_INCREMENT, PRIMARY KEY (assigned_id, '
1432
 
                            'id), KEY idx_autoinc_id (id))ENGINE=Inn'
1433
 
                            'oDB')
1434
 
 
1435
 
        t1 = Table('sometable', MetaData(), Column('assigned_id',
1436
 
                   Integer(), primary_key=True, autoincrement=True),
1437
 
                   Column('id', Integer(), primary_key=True,
1438
 
                   autoincrement=False), mysql_engine='InnoDB')
1439
 
        self.assert_compile(schema.CreateTable(t1),
1440
 
                            'CREATE TABLE sometable (assigned_id '
1441
 
                            'INTEGER NOT NULL AUTO_INCREMENT, id '
1442
 
                            'INTEGER NOT NULL, PRIMARY KEY '
1443
 
                            '(assigned_id, id))ENGINE=InnoDB')
1444
 
 
1445
 
    def test_innodb_autoincrement_reserved_word_column_name(self):
1446
 
        t1 = Table(
1447
 
            'sometable', MetaData(),
1448
 
            Column('id', Integer(), primary_key=True, autoincrement=False),
1449
 
            Column('order', Integer(), primary_key=True, autoincrement=True),
1450
 
            mysql_engine='InnoDB')
1451
 
        self.assert_compile(
1452
 
            schema.CreateTable(t1),
1453
 
            'CREATE TABLE sometable ('
1454
 
            'id INTEGER NOT NULL, '
1455
 
            '`order` INTEGER NOT NULL AUTO_INCREMENT, '
1456
 
            'PRIMARY KEY (id, `order`), '
1457
 
            'KEY idx_autoinc_order (`order`)'
1458
 
            ')ENGINE=InnoDB')
1459
 
 
1460
 
 
1461
 
class SQLModeDetectionTest(fixtures.TestBase):
1462
 
    __only_on__ = 'mysql'
1463
 
 
1464
 
    def _options(self, modes):
1465
 
        def connect(con, record):
1466
 
            cursor = con.cursor()
1467
 
            print "DOING THiS:", "set sql_mode='%s'" % (",".join(modes))
1468
 
            cursor.execute("set sql_mode='%s'" % (",".join(modes)))
1469
 
        e = engines.testing_engine(options={
1470
 
            'pool_events':[
1471
 
                (connect, 'first_connect'),
1472
 
                (connect, 'connect')
1473
 
            ]
1474
 
        })
1475
 
        return e
1476
 
 
1477
 
    def test_backslash_escapes(self):
1478
 
        engine = self._options(['NO_BACKSLASH_ESCAPES'])
1479
 
        c = engine.connect()
1480
 
        assert not engine.dialect._backslash_escapes
1481
 
        c.close()
1482
 
        engine.dispose()
1483
 
 
1484
 
        engine = self._options([])
1485
 
        c = engine.connect()
1486
 
        assert engine.dialect._backslash_escapes
1487
 
        c.close()
1488
 
        engine.dispose()
1489
 
 
1490
 
    def test_ansi_quotes(self):
1491
 
        engine = self._options(['ANSI_QUOTES'])
1492
 
        c = engine.connect()
1493
 
        assert engine.dialect._server_ansiquotes
1494
 
        c.close()
1495
 
        engine.dispose()
1496
 
 
1497
 
    def test_combination(self):
1498
 
        engine = self._options(['ANSI_QUOTES,NO_BACKSLASH_ESCAPES'])
1499
 
        c = engine.connect()
1500
 
        assert engine.dialect._server_ansiquotes
1501
 
        assert not engine.dialect._backslash_escapes
1502
 
        c.close()
1503
 
        engine.dispose()
1504
 
 
1505
 
class RawReflectionTest(fixtures.TestBase):
1506
 
    def setup(self):
1507
 
        dialect = mysql.dialect()
1508
 
        self.parser = mysql.MySQLTableDefinitionParser(dialect, dialect.identifier_preparer)
1509
 
 
1510
 
    def test_key_reflection(self):
1511
 
        regex = self.parser._re_key
1512
 
 
1513
 
        assert regex.match('  PRIMARY KEY (`id`),')
1514
 
        assert regex.match('  PRIMARY KEY USING BTREE (`id`),')
1515
 
        assert regex.match('  PRIMARY KEY (`id`) USING BTREE,')
1516
 
        assert regex.match('  PRIMARY KEY (`id`)')
1517
 
        assert regex.match('  PRIMARY KEY USING BTREE (`id`)')
1518
 
        assert regex.match('  PRIMARY KEY (`id`) USING BTREE')
1519
 
 
1520
 
 
1521
 
class ExecutionTest(fixtures.TestBase):
1522
 
    """Various MySQL execution special cases."""
1523
 
 
1524
 
    __only_on__ = 'mysql'
1525
 
 
1526
 
    def test_charset_caching(self):
1527
 
        engine = engines.testing_engine()
1528
 
 
1529
 
        cx = engine.connect()
1530
 
        meta = MetaData()
1531
 
        charset = engine.dialect._detect_charset(cx)
1532
 
 
1533
 
        meta.reflect(cx)
1534
 
        eq_(cx.dialect._connection_charset, charset)
1535
 
        cx.close()
1536
 
 
1537
 
    def test_sysdate(self):
1538
 
        d = testing.db.scalar(func.sysdate())
1539
 
        assert isinstance(d, datetime.datetime)
1540
 
 
1541
 
class MatchTest(fixtures.TestBase, AssertsCompiledSQL):
1542
 
    __only_on__ = 'mysql'
1543
 
 
1544
 
    @classmethod
1545
 
    def setup_class(cls):
1546
 
        global metadata, cattable, matchtable
1547
 
        metadata = MetaData(testing.db)
1548
 
 
1549
 
        cattable = Table('cattable', metadata,
1550
 
            Column('id', Integer, primary_key=True),
1551
 
            Column('description', String(50)),
1552
 
            mysql_engine='MyISAM'
1553
 
        )
1554
 
        matchtable = Table('matchtable', metadata,
1555
 
            Column('id', Integer, primary_key=True),
1556
 
            Column('title', String(200)),
1557
 
            Column('category_id', Integer, ForeignKey('cattable.id')),
1558
 
            mysql_engine='MyISAM'
1559
 
        )
1560
 
        metadata.create_all()
1561
 
 
1562
 
        cattable.insert().execute([
1563
 
            {'id': 1, 'description': 'Python'},
1564
 
            {'id': 2, 'description': 'Ruby'},
1565
 
        ])
1566
 
        matchtable.insert().execute([
1567
 
            {'id': 1,
1568
 
             'title': 'Agile Web Development with Rails',
1569
 
             'category_id': 2},
1570
 
            {'id': 2,
1571
 
             'title': 'Dive Into Python',
1572
 
             'category_id': 1},
1573
 
            {'id': 3,
1574
 
             'title': "Programming Matz's Ruby",
1575
 
             'category_id': 2},
1576
 
            {'id': 4,
1577
 
             'title': 'The Definitive Guide to Django',
1578
 
             'category_id': 1},
1579
 
            {'id': 5,
1580
 
             'title': 'Python in a Nutshell',
1581
 
             'category_id': 1}
1582
 
        ])
1583
 
 
1584
 
    @classmethod
1585
 
    def teardown_class(cls):
1586
 
        metadata.drop_all()
1587
 
 
1588
 
    @testing.fails_on('mysql+mysqlconnector', 'uses pyformat')
1589
 
    def test_expression(self):
1590
 
        format = testing.db.dialect.paramstyle == 'format' and '%s' or '?'
1591
 
        self.assert_compile(
1592
 
            matchtable.c.title.match('somstr'),
1593
 
            "MATCH (matchtable.title) AGAINST (%s IN BOOLEAN MODE)" % format)
1594
 
 
1595
 
    @testing.fails_on('mysql+mysqldb', 'uses format')
1596
 
    @testing.fails_on('mysql+pymysql', 'uses format')
1597
 
    @testing.fails_on('mysql+oursql', 'uses format')
1598
 
    @testing.fails_on('mysql+pyodbc', 'uses format')
1599
 
    @testing.fails_on('mysql+zxjdbc', 'uses format')
1600
 
    def test_expression(self):
1601
 
        format = '%(title_1)s'
1602
 
        self.assert_compile(
1603
 
            matchtable.c.title.match('somstr'),
1604
 
            "MATCH (matchtable.title) AGAINST (%s IN BOOLEAN MODE)" % format)
1605
 
 
1606
 
    def test_simple_match(self):
1607
 
        results = (matchtable.select().
1608
 
                   where(matchtable.c.title.match('python')).
1609
 
                   order_by(matchtable.c.id).
1610
 
                   execute().
1611
 
                   fetchall())
1612
 
        eq_([2, 5], [r.id for r in results])
1613
 
 
1614
 
    def test_simple_match_with_apostrophe(self):
1615
 
        results = (matchtable.select().
1616
 
                   where(matchtable.c.title.match("Matz's")).
1617
 
                   execute().
1618
 
                   fetchall())
1619
 
        eq_([3], [r.id for r in results])
1620
 
 
1621
 
    def test_or_match(self):
1622
 
        results1 = (matchtable.select().
1623
 
                    where(or_(matchtable.c.title.match('nutshell'),
1624
 
                              matchtable.c.title.match('ruby'))).
1625
 
                    order_by(matchtable.c.id).
1626
 
                    execute().
1627
 
                    fetchall())
1628
 
        eq_([3, 5], [r.id for r in results1])
1629
 
        results2 = (matchtable.select().
1630
 
                    where(matchtable.c.title.match('nutshell ruby')).
1631
 
                    order_by(matchtable.c.id).
1632
 
                    execute().
1633
 
                    fetchall())
1634
 
        eq_([3, 5], [r.id for r in results2])
1635
 
 
1636
 
 
1637
 
    def test_and_match(self):
1638
 
        results1 = (matchtable.select().
1639
 
                    where(and_(matchtable.c.title.match('python'),
1640
 
                               matchtable.c.title.match('nutshell'))).
1641
 
                    execute().
1642
 
                    fetchall())
1643
 
        eq_([5], [r.id for r in results1])
1644
 
        results2 = (matchtable.select().
1645
 
                    where(matchtable.c.title.match('+python +nutshell')).
1646
 
                    execute().
1647
 
                    fetchall())
1648
 
        eq_([5], [r.id for r in results2])
1649
 
 
1650
 
    def test_match_across_joins(self):
1651
 
        results = (matchtable.select().
1652
 
                   where(and_(cattable.c.id==matchtable.c.category_id,
1653
 
                              or_(cattable.c.description.match('Ruby'),
1654
 
                                  matchtable.c.title.match('nutshell')))).
1655
 
                   order_by(matchtable.c.id).
1656
 
                   execute().
1657
 
                   fetchall())
1658
 
        eq_([1, 3, 5], [r.id for r in results])
1659
 
 
1660
 
 
1661
 
def colspec(c):
1662
 
    return testing.db.dialect.ddl_compiler(
1663
 
                    testing.db.dialect, None).get_column_specification(c)
1664