From: Mike Bayer Date: Mon, 3 Oct 2011 19:19:07 +0000 (-0400) Subject: - a CREATE TABLE will put the COLLATE option X-Git-Tag: rel_0_7_3~15 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a9ebba3e1b135f1b5ac612844dce1e34e4d0eeee;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - a CREATE TABLE will put the COLLATE option after CHARSET, which appears to be part of MySQL's arbitrary rules regarding if it will actually work or not. [ticket:2225] - reflecting a MySQL table will ensure that the options added to the Table at the table.kwargs level have spaces converted to underscores. This is a slight behavioral change specifically to the "mysql_default_charset" option which previously would not be symmetrical. --- diff --git a/CHANGES b/CHANGES index f4f846ff9f..3377cf383b 100644 --- a/CHANGES +++ b/CHANGES @@ -236,6 +236,19 @@ CHANGES it was assumed that "current" schema was synonymous with the full search_path. [ticket:2249] +- mysql + - a CREATE TABLE will put the COLLATE option + after CHARSET, which appears to be part of + MySQL's arbitrary rules regarding if it will actually + work or not. [ticket:2225] + + - reflecting a MySQL table will ensure that the + options added to the Table at the table.kwargs + level have spaces converted to underscores. + This is a slight behavioral change specifically + to the "mysql_default_charset" option which + previously would not be symmetrical. + - mssql - Changes to attempt support of FreeTDS 0.91 with Pyodbc. This includes that string binds are sent as diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index 77ad901824..1d7d898496 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -211,7 +211,7 @@ from array import array as _array from sqlalchemy.engine import reflection from sqlalchemy.engine import base as engine_base, default from sqlalchemy import types as sqltypes - +from sqlalchemy.util import topological from sqlalchemy.types import DATE, DATETIME, BOOLEAN, TIME, \ BLOB, BINARY, VARBINARY @@ -1363,26 +1363,36 @@ class MySQLDDLCompiler(compiler.DDLCompiler): """Build table-level CREATE options like ENGINE and COLLATE.""" table_opts = [] - for k in table.kwargs: - if k.startswith('%s_' % self.dialect.name): - opt = k[len(self.dialect.name)+1:].upper() - - arg = table.kwargs[k] - if opt in _options_of_type_string: - arg = "'%s'" % arg.replace("\\", "\\\\").replace("'", "''") - - if opt in ('DATA_DIRECTORY', 'INDEX_DIRECTORY', - 'DEFAULT_CHARACTER_SET', 'CHARACTER_SET', - 'DEFAULT_CHARSET', - 'DEFAULT_COLLATE'): - opt = opt.replace('_', ' ') - - joiner = '=' - if opt in ('TABLESPACE', 'DEFAULT CHARACTER SET', - 'CHARACTER SET', 'COLLATE'): - joiner = ' ' - - table_opts.append(joiner.join((opt, arg))) + + opts = dict( + ( + k[len(self.dialect.name)+1:].upper(), + v + ) + for k, v in table.kwargs.items() + if k.startswith('%s_' % self.dialect.name) + ) + + for opt in topological.sort([ + ('DEFAULT_CHARSET', 'COLLATE'), + ('DEFAULT_CHARACTER_SET', 'COLLATE') + ], opts): + arg = opts[opt] + if opt in _options_of_type_string: + arg = "'%s'" % arg.replace("\\", "\\\\").replace("'", "''") + + if opt in ('DATA_DIRECTORY', 'INDEX_DIRECTORY', + 'DEFAULT_CHARACTER_SET', 'CHARACTER_SET', + 'DEFAULT_CHARSET', + 'DEFAULT_COLLATE'): + opt = opt.replace('_', ' ') + + joiner = '=' + if opt in ('TABLESPACE', 'DEFAULT CHARACTER SET', + 'CHARACTER SET', 'COLLATE'): + joiner = ' ' + + table_opts.append(joiner.join((opt, arg))) return ' '.join(table_opts) def visit_drop_index(self, drop): @@ -2252,7 +2262,7 @@ class MySQLTableDefinitionParser(object): options.pop(nope, None) for opt, val in options.items(): - state.table_options['%s_%s' % (self.dialect.name, opt)] = val + state.table_options['%s_%s' % (self.dialect.name, opt.replace(' ', '_'))] = val def _parse_column(self, line, state): """Extract column details. diff --git a/test/dialect/test_mysql.py b/test/dialect/test_mysql.py index fb20f44fdd..b8bcd573d4 100644 --- a/test/dialect/test_mysql.py +++ b/test/dialect/test_mysql.py @@ -331,6 +331,22 @@ class TypesTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiledSQL): raise charset_table.drop() + @testing.exclude('mysql', '<', (5, 0, 5), 'a 5.0+ feature') + @testing.provide_metadata + def test_charset_collate_table(self): + t = Table('foo', self.metadata, + Column('id', Integer), + mysql_default_charset='utf8', + mysql_collate='utf8_unicode_ci' + ) + t.create() + m2 = MetaData(testing.db) + t2 = Table('foo', m2, autoload=True) + eq_(t2.kwargs['mysql_collate'], 'utf8_unicode_ci') + # this is also testing that the names coming + # back get an underscore _ in them + eq_(t2.kwargs['mysql_default_charset'], 'utf8') + @testing.exclude('mysql', '<', (5, 0, 5), 'a 5.0+ feature') @testing.fails_on('mysql+oursql', 'some round trips fail, oursql bug ?') def test_bit_50(self): @@ -878,7 +894,7 @@ class ReflectionTest(fixtures.TestBase, AssertsExecutionResults): assert reflected.kwargs['mysql_engine'] == 'MEMORY' assert reflected.kwargs['mysql_comment'] == comment - assert reflected.kwargs['mysql_default charset'] == 'utf8' + assert reflected.kwargs['mysql_default_charset'] == 'utf8' assert reflected.kwargs['mysql_avg_row_length'] == '3' assert reflected.kwargs['mysql_connection'] == 'fish'