1
# -*- fill-column: 78 -*-
3
# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Michael Bayer mike_mp@zzzcomputing.com
2
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
6
4
# This module is part of SQLAlchemy and is released under
7
5
# the MIT License: http://www.opensource.org/licenses/mit-license.php
43
41
See the API documentation on individual drivers for details on connecting.
48
All of MySQL's standard types are supported. These can also be specified within
49
table metadata, for the purpose of issuing CREATE TABLE statements
50
which include MySQL-specific extensions. The types are available
51
from the module, as in::
53
from sqlalchemy.dialects import mysql
55
Table('mytable', metadata,
56
Column('id', Integer, primary_key=True),
57
Column('ittybittyblob', mysql.TINYBLOB),
58
Column('biggy', mysql.BIGINT(unsigned=True)))
60
See the API documentation on specific column types for further details.
62
43
Connection Timeouts
63
44
-------------------
254
235
self.unsigned = kw.pop('unsigned', False)
255
236
self.zerofill = kw.pop('zerofill', False)
256
237
super(_NumericType, self).__init__(**kw)
258
239
class _FloatType(_NumericType, sqltypes.Float):
259
240
def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
260
241
if isinstance(self, (REAL, DOUBLE)) and \
311
292
class NUMERIC(_NumericType, sqltypes.NUMERIC):
312
293
"""MySQL NUMERIC type."""
314
295
__visit_name__ = 'NUMERIC'
316
297
def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
317
298
"""Construct a NUMERIC.
335
316
class DECIMAL(_NumericType, sqltypes.DECIMAL):
336
317
"""MySQL DECIMAL type."""
338
319
__visit_name__ = 'DECIMAL'
340
321
def __init__(self, precision=None, scale=None, asdecimal=True, **kw):
341
322
"""Construct a DECIMAL.
356
337
super(DECIMAL, self).__init__(precision=precision, scale=scale,
357
338
asdecimal=asdecimal, **kw)
360
341
class DOUBLE(_FloatType):
361
342
"""MySQL DOUBLE type."""
558
539
def result_processor(self, dialect, coltype):
559
540
"""Convert a MySQL's 64 bit, variable length binary string to a long.
561
542
TODO: this is MySQL-db, pyodbc specific. OurSQL and mysqlconnector
562
543
already do this, so this logic should be moved to those dialects.
566
547
def process(value):
567
548
if value is not None:
730
711
super(LONGTEXT, self).__init__(**kwargs)
733
714
class VARCHAR(_StringType, sqltypes.VARCHAR):
734
715
"""MySQL VARCHAR type, for variable-length character data."""
817
798
__visit_name__ = 'NCHAR'
819
800
def __init__(self, length=None, **kwargs):
820
"""Construct an NCHAR. Arguments are:
801
"""Construct an NCHAR.
822
803
:param length: Maximum data length, in characters.
838
819
class TINYBLOB(sqltypes._Binary):
839
820
"""MySQL TINYBLOB type, for binary data up to 2^8 bytes."""
841
822
__visit_name__ = 'TINYBLOB'
843
824
class MEDIUMBLOB(sqltypes._Binary):
863
844
Column('myenum', MSEnum("foo", "bar", "baz"))
867
846
:param enums: The range of valid values for this ENUM. Values will be
868
847
quoted when generating the schema according to the quoting flag (see
940
919
kw.pop('native_enum', None)
941
920
_StringType.__init__(self, length=length, **kw)
942
921
sqltypes.Enum.__init__(self, *enums)
945
924
def _strip_enums(cls, enums):
950
929
a = a[1:-1].replace(a[0] * 2, a[0])
951
930
strip_enums.append(a)
952
931
return strip_enums
954
933
def bind_processor(self, dialect):
955
934
super_convert = super(ENUM, self).bind_processor(dialect)
956
935
def process(value):
976
955
Column('myset', MSSet("'foo'", "'bar'", "'baz'"))
980
957
:param values: The range of valid values for this SET. Values will be
981
958
used exactly as they appear when generating schemas. Strings must
982
959
be quoted, as in the example above. Single-quotes are suggested for
1140
1117
extract_map.update ({
1141
1118
'milliseconds': 'millisecond',
1144
1121
def visit_random_func(self, fn, **kw):
1145
1122
return "rand%s" % self.function_argspec(fn)
1147
1124
def visit_utc_timestamp_func(self, fn, **kw):
1148
1125
return "UTC_TIMESTAMP"
1150
1127
def visit_sysdate_func(self, fn, **kw):
1151
1128
return "SYSDATE()"
1153
1130
def visit_concat_op(self, binary, **kw):
1154
1131
return "concat(%s, %s)" % (self.process(binary.left), self.process(binary.right))
1156
1133
def visit_match_op(self, binary, **kw):
1157
1134
return "MATCH (%s) AGAINST (%s IN BOOLEAN MODE)" % (self.process(binary.left), self.process(binary.right))
1189
1166
# No cast until 4, no decimals until 5.
1190
1167
if not self.dialect._supports_cast:
1191
1168
return self.process(cast.clause)
1193
1170
type_ = self.process(cast.typeclause)
1194
1171
if type_ is None:
1195
1172
return self.process(cast.clause)
1201
1178
if self.dialect._backslash_escapes:
1202
1179
value = value.replace('\\', '\\\\')
1205
1182
def get_select_precolumns(self, select):
1206
1183
if isinstance(select._distinct, basestring):
1207
1184
return select._distinct.upper() + " "
1281
1258
def create_table_constraints(self, table):
1282
1259
"""Get table constraints."""
1283
1260
constraint_string = super(MySQLDDLCompiler, self).create_table_constraints(table)
1285
1262
is_innodb = table.kwargs.has_key('mysql_engine') and \
1286
1263
table.kwargs['mysql_engine'].lower() == 'innodb'
1308
1285
default = self.get_column_default_string(column)
1309
1286
if default is not None:
1310
1287
colspec.append('DEFAULT ' + default)
1312
1289
is_timestamp = isinstance(column.type, sqltypes.TIMESTAMP)
1313
1290
if not column.nullable and not is_timestamp:
1314
1291
colspec.append('NOT NULL')
1357
1334
def visit_drop_index(self, drop):
1358
1335
index = drop.element
1360
1337
return "\nDROP INDEX %s ON %s" % \
1361
(self.preparer.quote(self._validate_identifier(index.name, False), index.quote),
1338
(self.preparer.quote(self._index_identifier(index.name), index.quote),
1362
1339
self.preparer.format_table(index.table))
1364
1341
def visit_drop_constraint(self, drop):
1423
1400
if c is not None])
1424
1401
return ' '.join([c for c in (spec, charset, collation)
1425
1402
if c is not None])
1427
1404
def _mysql_type(self, type_):
1428
1405
return isinstance(type_, (_StringType, _NumericType))
1430
1407
def visit_NUMERIC(self, type_):
1431
1408
if type_.precision is None:
1432
1409
return self._extend_numeric(type_, "NUMERIC")
1458
1435
'scale' : type_.scale})
1460
1437
return self._extend_numeric(type_, 'REAL')
1462
1439
def visit_FLOAT(self, type_):
1463
1440
if self._mysql_type(type_) and type_.scale is not None and type_.precision is not None:
1464
1441
return self._extend_numeric(type_, "FLOAT(%s, %s)" % (type_.precision, type_.scale))
1466
1443
return self._extend_numeric(type_, "FLOAT(%s)" % (type_.precision,))
1468
1445
return self._extend_numeric(type_, "FLOAT")
1470
1447
def visit_INTEGER(self, type_):
1471
1448
if self._mysql_type(type_) and type_.display_width is not None:
1472
1449
return self._extend_numeric(type_, "INTEGER(%(display_width)s)" % {'display_width': type_.display_width})
1474
1451
return self._extend_numeric(type_, "INTEGER")
1476
1453
def visit_BIGINT(self, type_):
1477
1454
if self._mysql_type(type_) and type_.display_width is not None:
1478
1455
return self._extend_numeric(type_, "BIGINT(%(display_width)s)" % {'display_width': type_.display_width})
1480
1457
return self._extend_numeric(type_, "BIGINT")
1482
1459
def visit_MEDIUMINT(self, type_):
1483
1460
if self._mysql_type(type_) and type_.display_width is not None:
1484
1461
return self._extend_numeric(type_, "MEDIUMINT(%(display_width)s)" % {'display_width': type_.display_width})
1522
1499
return "YEAR(%s)" % type_.display_width
1524
1501
def visit_TEXT(self, type_):
1525
1502
if type_.length:
1526
1503
return self._extend_string(type_, {}, "TEXT(%d)" % type_.length)
1528
1505
return self._extend_string(type_, {}, "TEXT")
1530
1507
def visit_TINYTEXT(self, type_):
1531
1508
return self._extend_string(type_, {}, "TINYTEXT")
1533
1510
def visit_MEDIUMTEXT(self, type_):
1534
1511
return self._extend_string(type_, {}, "MEDIUMTEXT")
1536
1513
def visit_LONGTEXT(self, type_):
1537
1514
return self._extend_string(type_, {}, "LONGTEXT")
1539
1516
def visit_VARCHAR(self, type_):
1540
1517
if type_.length:
1541
1518
return self._extend_string(type_, {}, "VARCHAR(%d)" % type_.length)
1543
1520
raise exc.InvalidRequestError("VARCHAR requires a length when rendered on MySQL")
1545
1522
def visit_CHAR(self, type_):
1546
1523
if type_.length:
1547
1524
return self._extend_string(type_, {}, "CHAR(%(length)s)" % {'length' : type_.length})
1549
1526
return self._extend_string(type_, {}, "CHAR")
1551
1528
def visit_NVARCHAR(self, type_):
1552
1529
# We'll actually generate the equiv. "NATIONAL VARCHAR" instead
1553
1530
# of "NVARCHAR".
1555
1532
return self._extend_string(type_, {'national':True}, "VARCHAR(%(length)s)" % {'length': type_.length})
1557
1534
raise exc.InvalidRequestError("NVARCHAR requires a length when rendered on MySQL")
1559
1536
def visit_NCHAR(self, type_):
1560
1537
# We'll actually generate the equiv. "NATIONAL CHAR" instead of "NCHAR".
1561
1538
if type_.length:
1562
1539
return self._extend_string(type_, {'national':True}, "CHAR(%(length)s)" % {'length': type_.length})
1564
1541
return self._extend_string(type_, {'national':True}, "CHAR")
1566
1543
def visit_VARBINARY(self, type_):
1567
1544
return "VARBINARY(%d)" % type_.length
1569
1546
def visit_large_binary(self, type_):
1570
1547
return self.visit_BLOB(type_)
1572
1549
def visit_enum(self, type_):
1573
1550
if not type_.native_enum:
1574
1551
return super(MySQLTypeCompiler, self).visit_enum(type_)
1576
1553
return self.visit_ENUM(type_)
1578
1555
def visit_BLOB(self, type_):
1579
1556
if type_.length:
1580
1557
return "BLOB(%d)" % type_.length
1584
1561
def visit_TINYBLOB(self, type_):
1585
1562
return "TINYBLOB"
1595
1572
for e in type_.enums:
1596
1573
quoted_enums.append("'%s'" % e.replace("'", "''"))
1597
1574
return self._extend_string(type_, {}, "ENUM(%s)" % ",".join(quoted_enums))
1599
1576
def visit_SET(self, type_):
1600
1577
return self._extend_string(type_, {}, "SET(%s)" % ",".join(type_._ddl_values))
1602
1579
def visit_BOOLEAN(self, type):
1606
1583
class MySQLIdentifierPreparer(compiler.IdentifierPreparer):
1626
1603
class MySQLDialect(default.DefaultDialect):
1627
1604
"""Details of the MySQL dialect. Not used directly in application code."""
1630
1607
supports_alter = True
1631
1609
# identifiers are 64, however aliases can be 255...
1632
1610
max_identifier_length = 255
1611
max_index_name_length = 64
1634
1613
supports_native_enum = True
1636
1615
supports_sane_rowcount = True
1637
1616
supports_sane_multi_rowcount = False
1639
1618
default_paramstyle = 'format'
1640
1619
colspecs = colspecs
1642
1621
statement_compiler = MySQLCompiler
1643
1622
ddl_compiler = MySQLDDLCompiler
1644
1623
type_compiler = MySQLTypeCompiler
1645
1624
ischema_names = ischema_names
1646
1625
preparer = MySQLIdentifierPreparer
1648
1627
# default SQL compilation settings -
1649
1628
# these are modified upon initialize(),
1650
1629
# i.e. first connect
1651
1630
_backslash_escapes = True
1652
1631
_server_ansiquotes = False
1654
1633
def __init__(self, use_ansiquotes=None, **kwargs):
1655
1634
default.DefaultDialect.__init__(self, **kwargs)
1710
1689
if isinstance(e, self.dbapi.OperationalError):
1711
1690
return self._extract_error_code(e) in \
1712
1691
(2006, 2013, 2014, 2045, 2055)
1713
elif isinstance(e, self.dbapi.InterfaceError):
1692
elif isinstance(e, self.dbapi.InterfaceError):
1714
1693
# if underlying connection is closed,
1715
1694
# this is the error you get
1716
1695
return "(0, '')" in str(e)
1735
1714
def _extract_error_code(self, exception):
1736
1715
raise NotImplementedError()
1738
1717
def _get_default_schema_name(self, connection):
1739
1718
return connection.execute('SELECT DATABASE()').scalar()
1786
1765
def _supports_cast(self):
1787
1766
return self.server_version_info is None or \
1788
1767
self.server_version_info >= (4, 0, 2)
1790
1769
@reflection.cache
1791
1770
def get_schema_names(self, connection, **kw):
1792
1771
rp = connection.execute("SHOW schemas")
1812
1791
return [row[0] for row in self._compat_fetchall(rp, charset=charset)\
1813
1792
if row[1] == 'BASE TABLE']
1815
1794
@reflection.cache
1816
1795
def get_view_names(self, connection, schema=None, **kw):
1817
1796
charset = self._connection_charset
1891
1870
def get_indexes(self, connection, table_name, schema=None, **kw):
1893
1872
parsed_state = self._parsed_state_or_create(connection, table_name, schema, **kw)
1896
1875
for spec in parsed_state.keys:
1932
1911
info_cache=kw.get('info_cache', None)
1935
1914
@util.memoized_property
1936
1915
def _tabledef_parser(self):
1937
1916
"""return the MySQLTableDefinitionParser, generate if needed.
1939
1918
The deferred creation ensures that the dialect has
1940
1919
retrieved server version information first.
1943
1922
if (self.server_version_info < (4, 1) and self._server_ansiquotes):
1944
1923
# ANSI_QUOTES doesn't affect SHOW CREATE TABLE on < 4.1
1947
1926
preparer = self.identifier_preparer
1948
1927
return MySQLTableDefinitionParser(self, preparer)
1950
1929
@reflection.cache
1951
1930
def _setup_parser(self, connection, table_name, schema=None, **kw):
1952
1931
charset = self._connection_charset
1961
1940
full_name=full_name)
1962
1941
sql = parser._describe_to_create(table_name, columns)
1963
1942
return parser.parse(sql, charset)
1965
1944
def _adjust_casing(self, table, charset=None):
1966
1945
"""Adjust Table name to the server case sensitivity, if needed."""
2035
2014
mode = (mode_no | 4 == mode_no) and 'ANSI_QUOTES' or ''
2037
2016
self._server_ansiquotes = 'ANSI_QUOTES' in mode
2039
2018
# as of MySQL 5.0.1
2040
2019
self._backslash_escapes = 'NO_BACKSLASH_ESCAPES' not in mode
2042
2021
def _show_create_table(self, connection, table, charset=None,
2043
2022
full_name=None):
2044
2023
"""Run SHOW CREATE TABLE for a ``Table``."""
2088
2067
class ReflectedState(object):
2089
2068
"""Stores raw information about a SHOW CREATE TABLE statement."""
2091
2070
def __init__(self):
2092
2071
self.columns = []
2093
2072
self.table_options = {}
2094
2073
self.table_name = None
2096
2075
self.constraints = []
2098
2077
class MySQLTableDefinitionParser(object):
2099
2078
"""Parses the results of a SHOW CREATE TABLE statement."""
2101
2080
def __init__(self, dialect, preparer):
2102
2081
self.dialect = dialect
2103
2082
self.preparer = preparer
2390
2369
r'(?: +COLLATE +(?P<collate>[\w_]+))?'
2391
2370
r'(?: +(?P<notnull>NOT NULL))?'
2392
2371
r'(?: +DEFAULT +(?P<default>'
2393
r'(?:NULL|\x27(?:\x27\x27|[^\x27])*\x27|\w+)'
2394
r'(?:ON UPDATE \w+)?'
2372
r'(?:NULL|\x27(?:\x27\x27|[^\x27])*\x27|\w+'
2373
r'(?: +ON UPDATE \w+)?)'
2396
2375
r'(?: +(?P<autoincr>AUTO_INCREMENT))?'
2397
2376
r'(?: +COMMENT +(P<comment>(?:\x27\x27|[^\x27])+))?'