http://dev.mysql.com/doc/refman/5.0/en/create-table.html
+Index Parsers
+~~~~~~~~~~~~~
+
+CREATE FULLTEXT INDEX in MySQL also supports a "WITH PARSER" option. This
+is available using the keyword argument ``mysql_with_parser``::
+
+ Index(
+ 'my_index', my_table.c.data,
+ mysql_prefix='FULLTEXT', mysql_with_parser="ngram")
+
+.. versionadded:: 1.3
+
+
.. _mysql_foreign_keys:
MySQL Foreign Keys
columns = ', '.join(columns)
text += '(%s)' % columns
+ parser = index.dialect_options['mysql']['with_parser']
+ if parser is not None:
+ text += " WITH PARSER %s" % (parser, )
+
using = index.dialect_options['mysql']['using']
if using is not None:
text += " USING %s" % (preparer.quote(using))
"using": None,
"length": None,
"prefix": None,
+ "with_parser": None
})
]
connection, table_name, schema, **kw)
indexes = []
+
for spec in parsed_state.keys:
+ dialect_options = {}
unique = False
flavor = spec['type']
if flavor == 'PRIMARY':
continue
if flavor == 'UNIQUE':
unique = True
- elif flavor in (None, 'FULLTEXT', 'SPATIAL'):
+ elif flavor in ('FULLTEXT', 'SPATIAL'):
+ dialect_options["mysql_prefix"] = flavor
+ elif flavor is None:
pass
else:
self.logger.info(
"Converting unknown KEY type %s to a plain KEY", flavor)
pass
+
+ if spec['parser']:
+ dialect_options['mysql_with_parser'] = spec['parser']
+
index_d = {}
+ if dialect_options:
+ index_d["dialect_options"] = dialect_options
+
index_d['name'] = spec['name']
index_d['column_names'] = [s[0] for s in spec['columns']]
index_d['unique'] = unique
spec = m.groupdict()
# convert columns into name, length pairs
spec['columns'] = self._parse_keyexprs(spec['columns'])
+ if spec['version_sql']:
+ m2 = self._re_key_version_sql.match(spec['version_sql'])
+ if m2 and m2.groupdict()['parser']:
+ spec['parser'] = m2.groupdict()['parser']
+ if spec['parser']:
+ spec['parser'] = self.preparer.unformat_identifiers(
+ spec['parser'])[0]
return 'key', spec
# FOREIGN KEY CONSTRAINT
# (PRIMARY|UNIQUE|FULLTEXT|SPATIAL) INDEX `name` (USING (BTREE|HASH))?
# (`col` (ASC|DESC)?, `col` (ASC|DESC)?)
- # KEY_BLOCK_SIZE size | WITH PARSER name
+ # KEY_BLOCK_SIZE size | WITH PARSER name /*!50100 WITH PARSER name */
self._re_key = _re_compile(
r' '
r'(?:(?P<type>\S+) )?KEY'
r'(?: +KEY_BLOCK_SIZE *[ =]? *(?P<keyblock>\S+))?'
r'(?: +WITH PARSER +(?P<parser>\S+))?'
r'(?: +COMMENT +(?P<comment>(\x27\x27|\x27([^\x27])*?\x27)+))?'
+ r'(?: +/\*(?P<version_sql>.+)\*/ +)?'
r',?$'
% quotes
)
+ # https://forums.mysql.com/read.php?20,567102,567111#msg-567111
+ # It means if the MySQL version >= \d+, execute what's in the comment
+ self._re_key_version_sql = _re_compile(
+ r'\!\d+ '
+ r'(?: *WITH PARSER +(?P<parser>\S+) *)?'
+ )
+
# CONSTRAINT `name` FOREIGN KEY (`local_col`)
# REFERENCES `remote` (`remote_col`)
# MATCH FULL | MATCH PARTIAL | MATCH SIMPLE
from sqlalchemy import Column, Table, DDL, MetaData, TIMESTAMP, \
DefaultClause, String, Integer, Text, UnicodeText, SmallInteger,\
NCHAR, LargeBinary, DateTime, select, UniqueConstraint, Unicode,\
- BigInteger
+ BigInteger, Index
+from sqlalchemy.schema import CreateIndex
from sqlalchemy import event
from sqlalchemy import sql
from sqlalchemy import exc
from sqlalchemy import inspect
from sqlalchemy.dialects.mysql import base as mysql
from sqlalchemy.dialects.mysql import reflection as _reflection
-from sqlalchemy.testing import fixtures, AssertsExecutionResults
+from sqlalchemy.testing import fixtures, AssertsCompiledSQL
from sqlalchemy import testing
from sqlalchemy.testing import assert_raises_message, expect_warnings
import re
self._run_test(specs, ['enums'])
-class ReflectionTest(fixtures.TestBase, AssertsExecutionResults):
+class ReflectionTest(fixtures.TestBase, AssertsCompiledSQL):
__only_on__ = 'mysql'
__backend__ = True
self.assert_(indexes['uc_a'].unique)
self.assert_('uc_a' not in constraints)
+ @testing.provide_metadata
+ def test_reflect_fulltext(self):
+ mt = Table(
+ "mytable", self.metadata,
+ Column("id", Integer, primary_key=True),
+ Column("textdata", String(50)),
+ mysql_engine='InnoDB'
+ )
+ Index("textdata_ix", mt.c.textdata, mysql_prefix="FULLTEXT")
+ self.metadata.create_all(testing.db)
+
+ mt = Table(
+ "mytable", MetaData(), autoload_with=testing.db
+ )
+ idx = list(mt.indexes)[0]
+ eq_(idx.name, "textdata_ix")
+ eq_(idx.dialect_options['mysql']['prefix'], "FULLTEXT")
+ self.assert_compile(
+ CreateIndex(idx),
+ "CREATE FULLTEXT INDEX textdata_ix ON mytable (textdata)"
+ )
+
+ @testing.requires.mysql_ngram_fulltext
+ @testing.provide_metadata
+ def test_reflect_fulltext_comment(self):
+ mt = Table(
+ "mytable", self.metadata,
+ Column("id", Integer, primary_key=True),
+ Column("textdata", String(50)),
+ mysql_engine='InnoDB'
+ )
+ Index(
+ "textdata_ix", mt.c.textdata,
+ mysql_prefix="FULLTEXT", mysql_with_parser="ngram")
+
+ self.metadata.create_all(testing.db)
+
+ mt = Table(
+ "mytable", MetaData(), autoload_with=testing.db
+ )
+ idx = list(mt.indexes)[0]
+ eq_(idx.name, "textdata_ix")
+ eq_(idx.dialect_options['mysql']['prefix'], "FULLTEXT")
+ eq_(idx.dialect_options['mysql']['with_parser'], "ngram")
+ self.assert_compile(
+ CreateIndex(idx),
+ "CREATE FULLTEXT INDEX textdata_ix ON mytable "
+ "(textdata) WITH PARSER ngram"
+ )
+
class RawReflectionTest(fixtures.TestBase):
__backend__ = True
" KEY (`id`) USING BTREE COMMENT 'prefix''suffix'")
assert regex.match(
" KEY (`id`) USING BTREE COMMENT 'prefix''text''suffix'")
+ # https://forums.mysql.com/read.php?20,567102,567111#msg-567111
+ # "It means if the MySQL version >= 501, execute what's in the comment"
+ assert regex.match(
+ " FULLTEXT KEY `ix_fulltext_oi_g_name` (`oi_g_name`) "
+ "/*!50100 WITH PARSER `ngram` */ "
+ )
def test_fk_reflection(self):
regex = self.parser._re_fk_constraint