[Sqlalchemy-commits] sqlalchemy: - Changed the scheme used to generate truncated
Brought to you by:
zzzeek
From: <co...@sq...> - 2010-07-21 15:34:04
|
details: http://hg.sqlalchemy.org/sqlalchemy/sqlalchemy/rev/42cc5b99a100 changeset: 6703:42cc5b99a100 user: zzzeek date: Wed Jul 21 11:33:47 2010 -0400 description: - Changed the scheme used to generate truncated "auto" index names when using the "index=True" flag on Column. The truncation only takes place with the auto-generated name, not one that is user-defined (an error would be raised instead), and the truncation scheme itself is now based on a fragment of an md5 hash of the identifier name, so that multiple indexes on columns with similar names still have unique names. [ticket:1855] diffstat: CHANGES | 16 +++++++++++++- lib/sqlalchemy/__init__.py | 2 +- lib/sqlalchemy/dialects/access/base.py | 2 +- lib/sqlalchemy/dialects/mssql/base.py | 2 +- lib/sqlalchemy/dialects/mysql/base.py | 2 +- lib/sqlalchemy/dialects/postgresql/base.py | 2 +- lib/sqlalchemy/dialects/sqlite/base.py | 2 +- lib/sqlalchemy/dialects/sybase/base.py | 2 +- lib/sqlalchemy/schema.py | 2 +- lib/sqlalchemy/sql/compiler.py | 30 +++++++++++++------------- lib/sqlalchemy/util.py | 12 ++++++++++ test/sql/test_constraints.py | 33 ++++++++++++++++++++--------- 12 files changed, 73 insertions(+), 34 deletions(-) diffs (256 lines): diff -r 272663acedbb -r 42cc5b99a100 CHANGES --- a/CHANGES Tue Jul 20 11:55:12 2010 -0400 +++ b/CHANGES Wed Jul 21 11:33:47 2010 -0400 @@ -3,6 +3,20 @@ ======= CHANGES ======= +0.6.4 +===== +- sql + - Changed the scheme used to generate truncated + "auto" index names when using the "index=True" + flag on Column. The truncation only takes + place with the auto-generated name, not one + that is user-defined (an error would be + raised instead), and the truncation scheme + itself is now based on a fragment of an md5 + hash of the identifier name, so that multiple + indexes on columns with similar names still + have unique names. [ticket:1855] + 0.6.3 ===== - orm @@ -38,7 +52,7 @@ naming/typing information about the entities the Query will return. Can be helpful for building GUIs on top of ORM queries. - + - mysql - The _extract_error_code() method now works diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/__init__.py --- a/lib/sqlalchemy/__init__.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/__init__.py Wed Jul 21 11:33:47 2010 -0400 @@ -114,6 +114,6 @@ __all__ = sorted(name for name, obj in locals().items() if not (name.startswith('_') or inspect.ismodule(obj))) -__version__ = '0.6.3' +__version__ = '0.6.4' del inspect, sys diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/dialects/access/base.py --- a/lib/sqlalchemy/dialects/access/base.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/dialects/access/base.py Wed Jul 21 11:33:47 2010 -0400 @@ -436,7 +436,7 @@ index = drop.element self.append("\nDROP INDEX [%s].[%s]" % \ (index.table.name, - self._validate_identifier(index.name, False))) + self._index_identifier(index.name))) class AccessIdentifierPreparer(compiler.IdentifierPreparer): reserved_words = compiler.RESERVED_WORDS.copy() diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/dialects/mssql/base.py --- a/lib/sqlalchemy/dialects/mssql/base.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/dialects/mssql/base.py Wed Jul 21 11:33:47 2010 -0400 @@ -967,7 +967,7 @@ return "\nDROP INDEX %s.%s" % ( self.preparer.quote_identifier(drop.element.table.name), self.preparer.quote( - self._validate_identifier(drop.element.name, False), + self._index_identifier(drop.element.name), drop.element.quote) ) diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/dialects/mysql/base.py --- a/lib/sqlalchemy/dialects/mysql/base.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/dialects/mysql/base.py Wed Jul 21 11:33:47 2010 -0400 @@ -1358,7 +1358,7 @@ index = drop.element return "\nDROP INDEX %s ON %s" % \ - (self.preparer.quote(self._validate_identifier(index.name, False), index.quote), + (self.preparer.quote(self._index_identifier(index.name), index.quote), self.preparer.format_table(index.table)) def visit_drop_constraint(self, drop): diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/dialects/postgresql/base.py --- a/lib/sqlalchemy/dialects/postgresql/base.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/dialects/postgresql/base.py Wed Jul 21 11:33:47 2010 -0400 @@ -480,7 +480,7 @@ text += "UNIQUE " text += "INDEX %s ON %s (%s)" \ % (preparer.quote( - self._validate_identifier(index.name, True), index.quote), + self._index_identifier(index.name), index.quote), preparer.format_table(index.table), ', '.join([preparer.format_column(c) for c in index.columns])) diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/dialects/sqlite/base.py --- a/lib/sqlalchemy/dialects/sqlite/base.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/dialects/sqlite/base.py Wed Jul 21 11:33:47 2010 -0400 @@ -280,7 +280,7 @@ text += "UNIQUE " text += "INDEX %s ON %s (%s)" \ % (preparer.format_index(index, - name=self._validate_identifier(index.name, True)), + name=self._index_identifier(index.name)), preparer.format_table(index.table, use_schema=False), ', '.join(preparer.quote(c.name, c.quote) for c in index.columns)) diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/dialects/sybase/base.py --- a/lib/sqlalchemy/dialects/sybase/base.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/dialects/sybase/base.py Wed Jul 21 11:33:47 2010 -0400 @@ -356,7 +356,7 @@ return "\nDROP INDEX %s.%s" % ( self.preparer.quote_identifier(index.table.name), self.preparer.quote( - self._validate_identifier(index.name, False), index.quote) + self._index_identifier(index.name), index.quote) ) class SybaseIdentifierPreparer(compiler.IdentifierPreparer): diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/schema.py --- a/lib/sqlalchemy/schema.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/schema.py Wed Jul 21 11:33:47 2010 -0400 @@ -825,7 +825,7 @@ "The 'index' keyword argument on Column is boolean only. " "To create indexes with a specific name, create an " "explicit Index object external to the Table.") - Index('ix_%s' % self._label, self, unique=self.unique) + Index(expression._generated_label('ix_%s' % self._label), self, unique=self.unique) elif self.unique: if isinstance(self.unique, basestring): raise exc.ArgumentError( diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/sql/compiler.py --- a/lib/sqlalchemy/sql/compiler.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/sql/compiler.py Wed Jul 21 11:33:47 2010 -0400 @@ -1179,7 +1179,18 @@ def visit_drop_table(self, drop): return "\nDROP TABLE " + self.preparer.format_table(drop.element) - + + def _index_identifier(self, ident): + if isinstance(ident, sql._generated_label): + if len(ident) > self.dialect.max_identifier_length: + return ident[0:self.dialect.max_identifier_length - 8] + \ + "_" + util.md5_hex(ident)[-4:] + else: + return ident + else: + self.dialect.validate_identifier(ident) + return ident + def visit_create_index(self, create): index = create.element preparer = self.preparer @@ -1187,7 +1198,8 @@ if index.unique: text += "UNIQUE " text += "INDEX %s ON %s (%s)" \ - % (preparer.quote(self._validate_identifier(index.name, True), index.quote), + % (preparer.quote(self._index_identifier(index.name), + index.quote), preparer.format_table(index.table), ', '.join(preparer.quote(c.name, c.quote) for c in index.columns)) @@ -1196,7 +1208,7 @@ def visit_drop_index(self, drop): index = drop.element return "\nDROP INDEX " + \ - self.preparer.quote(self._validate_identifier(index.name, False), index.quote) + self.preparer.quote(self._index_identifier(index.name), index.quote) def visit_add_constraint(self, create): preparer = self.preparer @@ -1238,18 +1250,6 @@ def post_create_table(self, table): return '' - def _validate_identifier(self, ident, truncate): - if truncate: - if len(ident) > self.dialect.max_identifier_length: - counter = getattr(self, 'counter', 0) - self.counter = counter + 1 - return ident[0:self.dialect.max_identifier_length - 6] + "_" + hex(self.counter)[2:] - else: - return ident - else: - self.dialect.validate_identifier(ident) - return ident - def get_column_default_string(self, column): if isinstance(column.server_default, schema.DefaultClause): if isinstance(column.server_default.arg, basestring): diff -r 272663acedbb -r 42cc5b99a100 lib/sqlalchemy/util.py --- a/lib/sqlalchemy/util.py Tue Jul 20 11:55:12 2010 -0400 +++ b/lib/sqlalchemy/util.py Wed Jul 21 11:33:47 2010 -0400 @@ -216,6 +216,18 @@ return func(*(args + fargs), **newkeywords) return newfunc +try: + import hashlib + _md5 = hashlib.md5 +except ImportError: + import md5 + _md5 = md5.new + +def md5_hex(x): + m = _md5() + m.update(x) + return m.hexdigest() + def accepts_a_list_as_starargs(list_deprecation=None): def decorate(fn): diff -r 272663acedbb -r 42cc5b99a100 test/sql/test_constraints.py --- a/test/sql/test_constraints.py Tue Jul 20 11:55:12 2010 -0400 +++ b/test/sql/test_constraints.py Wed Jul 21 11:33:47 2010 -0400 @@ -183,18 +183,31 @@ def test_too_long_idx_name(self): dialect = testing.db.dialect.__class__() - dialect.max_identifier_length = 20 + dialect.max_identifier_length = 22 - t1 = Table("sometable", MetaData(), Column("foo", Integer)) - self.assert_compile( - schema.CreateIndex(Index("this_name_is_too_long_for_what_were_doing", t1.c.foo)), - "CREATE INDEX this_name_is_t_1 ON sometable (foo)", - dialect=dialect - ) + for tname, cname, exp in [ + ('sometable', 'this_name_is_too_long', 'ix_sometable_t_09aa'), + ('sometable', 'this_name_alsois_long', 'ix_sometable_t_3cf1'), + ]: - self.assert_compile( - schema.CreateIndex(Index("this_other_name_is_too_long_for_what_were_doing", t1.c.foo)), - "CREATE INDEX this_other_nam_1 ON sometable (foo)", + t1 = Table(tname, MetaData(), + Column(cname, Integer, index=True), + ) + ix1 = list(t1.indexes)[0] + + self.assert_compile( + schema.CreateIndex(ix1), + "CREATE INDEX %s " + "ON %s (%s)" % (exp, tname, cname), + dialect=dialect + ) + + t1 = Table('t', MetaData(), Column('c', Integer)) + assert_raises( + exc.IdentifierError, + schema.CreateIndex(Index( + "this_other_name_is_too_long_for_what_were_doing", + t1.c.c)).compile, dialect=dialect ) |