MySQL's arbitrary rules regarding if it will actually
work or not. [ticket:2225] Also in 0.6.9.
+ - Added mysql_length parameter to Index construct,
+ specifies "length" for indexes. [ticket:2293]
+
- mssql
- Changes to attempt support of FreeTDS 0.91 with
Pyodbc. This includes that string binds are sent as
fully rendering CAST; else the internal element of the
construct is rendered directly.
+
+.. _mysql_indexes:
+
+MySQL Specific Index Options
+----------------------------
+
+MySQL-specific extensions to the :class:`.Index` construct are available.
+
+Index Length
+~~~~~~~~~~~~~
+
+MySQL provides an option to create index entries with a certain length, where
+"length" refers to the number of characters or bytes in each value which will
+become part of the index. SQLAlchemy provides this feature via the
+``mysql_length`` parameter::
+
+ Index('my_index', my_table.c.data, mysql_length=10)
+
+Prefix lengths are given in characters for nonbinary string types and in bytes
+for binary string types. The value passed to the keyword argument will be
+simply passed through to the underlying CREATE INDEX command, so it *must* be
+an integer. MySQL only allows a length for an index if it is for a CHAR,
+VARCHAR, TEXT, BINARY, VARBINARY and BLOB.
+
+More information can be found at:
+http://dev.mysql.com/doc/refman/5.0/en/create-index.html
"""
import datetime, inspect, re, sys
table_opts.append(joiner.join((opt, arg)))
return ' '.join(table_opts)
+ def visit_create_index(self, create):
+ index = create.element
+ preparer = self.preparer
+ text = "CREATE "
+ if index.unique:
+ text += "UNIQUE "
+ text += "INDEX %s ON %s " \
+ % (preparer.quote(self._index_identifier(index.name),
+ index.quote),preparer.format_table(index.table))
+ if 'mysql_length' in index.kwargs:
+ length = index.kwargs['mysql_length']
+ else:
+ length = None
+ if length is not None:
+ text+= "(%s(%d))" \
+ % (', '.join(preparer.quote(c.name, c.quote)
+ for c in index.columns), length)
+ else:
+ text+= "(%s)" \
+ % (', '.join(preparer.quote(c.name, c.quote)
+ for c in index.columns))
+ return text
+
+
def visit_drop_index(self, drop):
index = drop.element
:ref:`schema_indexes` - General information on :class:`.Index`.
:ref:`postgresql_indexes` - PostgreSQL-specific options available for the :class:`.Index` construct.
+
+ :ref:`mysql_indexes` - MySQL-specific options available for the :class:`.Index` construct.
"""
__visit_name__ = 'index'
self.assert_compile(x,
'''SELECT mysql_table.col1, mysql_table.`master_ssl_verify_server_cert` FROM mysql_table''')
+ def test_create_index_with_length(self):
+ m = MetaData()
+ tbl = Table('testtbl', m, Column('data', String(255)))
+ idx = Index('test_idx1', tbl.c.data,
+ mysql_length=10)
+ idx2 = Index('test_idx2', tbl.c.data,
+ mysql_length=5)
+
+ self.assert_compile(schema.CreateIndex(idx),
+ 'CREATE INDEX test_idx1 ON testtbl (data(10))',
+ dialect=mysql.dialect())
+ self.assert_compile(schema.CreateIndex(idx2),
+ "CREATE INDEX test_idx2 ON testtbl (data(5))",
+ dialect=mysql.dialect())
+
class DialectTest(fixtures.TestBase):
__only_on__ = 'mysql'
Table('ai_1', meta,
Column('int_y', Integer, primary_key=True),
Column('int_n', Integer, DefaultClause('0'),
- primary_key=True))
+ primary_key=True),
+ mysql_engine='MyISAM')
Table('ai_2', meta,
Column('int_y', Integer, primary_key=True),
Column('int_n', Integer, DefaultClause('0'),
- primary_key=True))
+ primary_key=True),
+ mysql_engine='MyISAM')
Table('ai_3', meta,
Column('int_n', Integer, DefaultClause('0'),
primary_key=True, autoincrement=False),
- Column('int_y', Integer, primary_key=True))
+ Column('int_y', Integer, primary_key=True),
+ mysql_engine='MyISAM')
Table('ai_4', meta,
Column('int_n', Integer, DefaultClause('0'),
primary_key=True, autoincrement=False),
Column('int_n2', Integer, DefaultClause('0'),
- primary_key=True, autoincrement=False))
+ primary_key=True, autoincrement=False),
+ mysql_engine='MyISAM')
Table('ai_5', meta,
Column('int_y', Integer, primary_key=True),
Column('int_n', Integer, DefaultClause('0'),
- primary_key=True, autoincrement=False))
+ primary_key=True, autoincrement=False),
+ mysql_engine='MyISAM')
Table('ai_6', meta,
Column('o1', String(1), DefaultClause('x'),
primary_key=True),
- Column('int_y', Integer, primary_key=True))
+ Column('int_y', Integer, primary_key=True),
+ mysql_engine='MyISAM')
Table('ai_7', meta,
Column('o1', String(1), DefaultClause('x'),
primary_key=True),
Column('o2', String(1), DefaultClause('x'),
primary_key=True),
- Column('int_y', Integer, primary_key=True))
+ Column('int_y', Integer, primary_key=True),
+ mysql_engine='MyISAM')
Table('ai_8', meta,
Column('o1', String(1), DefaultClause('x'),
primary_key=True),
Column('o2', String(1), DefaultClause('x'),
- primary_key=True))
+ primary_key=True),
+ mysql_engine='MyISAM')
meta.create_all()
table_names = ['ai_1', 'ai_2', 'ai_3', 'ai_4',
cattable = Table('cattable', metadata,
Column('id', Integer, primary_key=True),
Column('description', String(50)),
+ mysql_engine='MyISAM'
)
matchtable = Table('matchtable', metadata,
Column('id', Integer, primary_key=True),
Column('title', String(200)),
Column('category_id', Integer, ForeignKey('cattable.id')),
+ mysql_engine='MyISAM'
)
metadata.create_all()
t1 = Table('test', meta,
Column('id', sa.Integer, primary_key=True),
Column('data', sa.String(50)),
+ mysql_engine='MyISAM'
)
t2 = Table('test2', meta,
Column('id', sa.Integer, sa.ForeignKey('test.id'),
primary_key=True),
Column('id2', sa.Integer, primary_key=True),
Column('data', sa.String(50)),
+ mysql_engine='MyISAM'
)
meta.create_all()
try:
assert t1.c.x is not None
@testing.fails_if(lambda:
- testing.against('mysql') and
- not testing.requires._has_mysql_fully_case_sensitive())
+ testing.against(('mysql', '<', (5, 5))) and
+ not testing.requires._has_mysql_fully_case_sensitive()
+ )
def test_reflect_via_fk(self):
m = MetaData()
t2 = Table("SomeOtherTable", m, autoload=True, autoload_with=testing.db)
# PG emergency shutdown:
# select * from pg_prepared_xacts
# ROLLBACK PREPARED '<xid>'
+ @testing.crashes('mysql', 'Crashing on 5.5, not worth it')
@testing.requires.skip_mysql_on_windows
@testing.requires.two_phase_transactions
@testing.requires.savepoints
test_needs_autoincrement=True),
Column('hoho', hohotype, server_default=str(hohoval)),
Column('counter', Integer, default=sa.func.char_length("1234567", type_=Integer)),
- Column('foober', String(30), default="im foober", onupdate="im the update"))
+ Column('foober', String(30), default="im foober", onupdate="im the update"),
+ mysql_engine='MyISAM')
st = Table('secondary_table', metadata,
Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
- Column('data', String(50)))
+ Column('data', String(50)),
+ mysql_engine='MyISAM')
if testing.against('postgresql', 'oracle'):
dt.append_column(
@testing.fails_on('sqlite', "sqlite autoincremnt doesn't work with composite pks")
def test_misordered_lastrow(self):
related = Table('related', metadata,
- Column('id', Integer, primary_key=True)
+ Column('id', Integer, primary_key=True),
+ mysql_engine='MyISAM'
)
t6 = Table("t6", metadata,
Column('manual_id', Integer, ForeignKey('related.id'), primary_key=True),
Column('auto_id', Integer, primary_key=True, test_needs_autoincrement=True),
+ mysql_engine='MyISAM'
)
metadata.create_all()