From: Mike Bayer Date: Wed, 31 Mar 2010 17:01:40 +0000 (-0400) Subject: - Restored some bind-labeling logic from 0.5 which ensures X-Git-Tag: rel_0_6_0~23^2~15^2~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5c758932a512e281308d2e1c147e18ed4eddc6b0;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Restored some bind-labeling logic from 0.5 which ensures that tables with column names that overlap another column of the form "_" won't produce errors if column._label is used as a bind name during an UPDATE. Test coverage which wasn't present in 0.5 has been added. [ticket:1755] --- diff --git a/CHANGES b/CHANGES index cca3298f07..cc3d9b6195 100644 --- a/CHANGES +++ b/CHANGES @@ -27,6 +27,14 @@ CHANGES "delete-orphan", or will simply detach it otherwise. [ticket:1754] +- sql + - Restored some bind-labeling logic from 0.5 which ensures + that tables with column names that overlap another column + of the form "_" won't produce + errors if column._label is used as a bind name during + an UPDATE. Test coverage which wasn't present in 0.5 + has been added. [ticket:1755] + - ext - the compiler extension now allows @compiles decorators on base classes that extend to child classes, @compiles diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 3aaa06fd6e..5958a0bc40 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -3185,6 +3185,16 @@ class ColumnClause(_Immutable, ColumnElement): label = _escape_for_generated(self.table.name) + "_" + \ _escape_for_generated(self.name) + # ensure the label name doesn't conflict with that + # of an existing column + if label in self.table.c: + _label = label + counter = 1 + while _label in self.table.c: + _label = label + "_" + str(counter) + counter += 1 + label = _label + return _generated_label(label) else: diff --git a/test/orm/test_unitofwork.py b/test/orm/test_unitofwork.py index fe4fac89e2..9cc12cffe5 100644 --- a/test/orm/test_unitofwork.py +++ b/test/orm/test_unitofwork.py @@ -795,6 +795,39 @@ class ExtraPassiveDeletesTest(_base.MappedTest): assert_raises(sa.exc.DBAPIError, session.flush) +class ColumnCollisionTest(_base.MappedTest): + """Ensure the mapper doesn't break bind param naming rules on flush.""" + + @classmethod + def define_tables(cls, metadata): + Table('book', metadata, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), + Column('book_id', String(50)), + Column('title', String(50)) + ) + + @testing.resolve_artifact_names + def test_naming(self): + class Book(_base.ComparableEntity): + pass + + mapper(Book, book) + sess = create_session() + + b1 = Book(book_id='abc', title='def') + sess.add(b1) + sess.flush() + + b1.title = 'ghi' + sess.flush() + sess.close() + eq_( + sess.query(Book).first(), + Book(book_id='abc', title='ghi') + ) + + + class DefaultTest(_base.MappedTest): """Exercise mappings on columns with DefaultGenerators. diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py index 5d61cbdec9..9490aef378 100644 --- a/test/sql/test_compiler.py +++ b/test/sql/test_compiler.py @@ -2095,6 +2095,20 @@ class CRUDTest(TestBase, AssertsCompiledSQL): params={'x':1, 'y':2}) self.assert_compile(i, "INSERT INTO foo (x, y) VALUES ((:param_1 + :x2), :y)", params={'x2':1, 'y':2}) + + def test_labels_no_collision(self): + + t = table('foo', column('id'), column('foo_id')) + + self.assert_compile( + t.update().where(t.c.id==5), + "UPDATE foo SET id=:id, foo_id=:foo_id WHERE foo.id = :id_1" + ) + + self.assert_compile( + t.update().where(t.c.id==bindparam(key=t.c.id._label)), + "UPDATE foo SET id=:id, foo_id=:foo_id WHERE foo.id = :foo_id_1" + ) class InlineDefaultTest(TestBase, AssertsCompiledSQL): def test_insert(self):