]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Restored some bind-labeling logic from 0.5 which ensures
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 31 Mar 2010 17:01:40 +0000 (13:01 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 31 Mar 2010 17:01:40 +0000 (13:01 -0400)
that tables with column names that overlap another column
of the form "<tablename>_<columnname>" 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]

CHANGES
lib/sqlalchemy/sql/expression.py
test/orm/test_unitofwork.py
test/sql/test_compiler.py

diff --git a/CHANGES b/CHANGES
index cca3298f072a0cbbd11d8c0e705899c2a08b0709..cc3d9b61951729b5a32fc5108f1c59897f4ca00f 100644 (file)
--- 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 "<tablename>_<columnname>" 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
index 3aaa06fd6e56be8c1ecfbce3b2320999f7ab286b..5958a0bc40c780f18672bbea11a73853bcdbed03 100644 (file)
@@ -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:
index fe4fac89e2025a3ce5b8858748789d645efc797a..9cc12cffe568ce131001f4ee17b6d8e835b31174 100644 (file)
@@ -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.
 
index 5d61cbdec9bf2103b5ce991c5ba514838035e4c9..9490aef378c2d2baef820aa3e7f0d442725d5aa0 100644 (file)
@@ -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):