]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- [bug] UPDATE..FROM syntax with SQL Server
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 18 Apr 2012 23:52:58 +0000 (19:52 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 18 Apr 2012 23:52:58 +0000 (19:52 -0400)
requires that the updated table be present
in the FROM clause when an alias of that
table is also present in the FROM clause.
The updated table is now always present
in the FROM, when FROM is present
in the first place.  Courtesy sayap.
[ticket:2468]

CHANGES
lib/sqlalchemy/dialects/mssql/base.py
lib/sqlalchemy/sql/compiler.py
test/dialect/test_mssql.py
test/sql/test_compiler.py
test/sql/test_update.py

diff --git a/CHANGES b/CHANGES
index 83408f68d5ffe156404f2e30d43a56c52a427913..1fb2557de5936264591f9c004747d65ca7d9448e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -68,6 +68,15 @@ CHANGES
     INSERT to get at the last inserted ID,
     for those tables which have "implicit_returning"
     set to False.
+  - [bug] UPDATE..FROM syntax with SQL Server
+    requires that the updated table be present
+    in the FROM clause when an alias of that
+    table is also present in the FROM clause.
+    The updated table is now always present
+    in the FROM, when FROM is present 
+    in the first place.  Courtesy sayap.
+    [ticket:2468]
 
 - postgresql
   - [feature] Added new for_update/with_lockmode()
index 78da18711a067fbc52b0611d602fb6d4794e9ea5..3366d5fab78bb0f3030b1b57db070aa0b1ddcb05 100644 (file)
@@ -985,6 +985,22 @@ class MSSQLCompiler(compiler.SQLCompiler):
         else:
             return ""
 
+    def update_from_clause(self, update_stmt,
+                                from_table, extra_froms,
+                                from_hints,
+                                **kw):
+        """Render the UPDATE..FROM clause specific to MSSQL.
+        
+        In MSSQL, if the UPDATE statement involves an alias of the table to
+        be updated, then the table itself must be added to the FROM list as
+        well. Otherwise, it is optional. Here, we add it regardless.
+        
+        """
+        return "FROM " + ', '.join(
+                    t._compiler_dispatch(self, asfrom=True,
+                                    fromhints=from_hints, **kw)
+                    for t in [from_table] + extra_froms)
+
 class MSSQLStrictCompiler(MSSQLCompiler):
     """A subclass of MSSQLCompiler which disables the usage of bind
     parameters where not allowed natively by MS-SQL.
index fdff99fb1267a36d3944e9bd1850f49b2968c664..bf234fe5cc16df1055315ac5d870ca0237383f28 100644 (file)
@@ -1115,7 +1115,7 @@ class SQLCompiler(engine.Compiled):
         """Provide a hook to override the generation of an 
         UPDATE..FROM clause.
 
-        MySQL overrides this.
+        MySQL and MSSQL override this.
 
         """
         return "FROM " + ', '.join(
index 2b35ff57fb65868600b23e165aff30184ca6be31..74e96c8efa53e56c60dd0f5b1a4b67f319b7356f 100644 (file)
@@ -136,7 +136,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
                                 selectable=t2, 
                                 dialect_name=darg),
                 "UPDATE sometable SET somecolumn=:somecolumn "
-                "FROM othertable WITH (PAGLOCK) "
+                "FROM sometable, othertable WITH (PAGLOCK) "
                 "WHERE sometable.somecolumn = othertable.somecolumn"
             )
 
index c3cf001fa634cd411f8be3a9272b19381ce93875..feb7405db5ddb7c826d4fdc5f06564e7b4d69ecf 100644 (file)
@@ -2926,6 +2926,19 @@ class CRUDTest(fixtures.TestBase, AssertsCompiledSQL):
                 "UPDATE mytable SET name=:name "
                 "FROM myothertable WHERE myothertable.otherid = mytable.myid")
 
+        self.assert_compile(u,
+                "UPDATE mytable SET name=:name "
+                "FROM mytable, myothertable WHERE "
+                "myothertable.otherid = mytable.myid",
+                dialect=mssql.dialect())
+
+        self.assert_compile(u.where(table2.c.othername == mt.c.name),
+                "UPDATE mytable SET name=:name "
+                "FROM mytable, myothertable, mytable AS mytable_1 "
+                "WHERE myothertable.otherid = mytable.myid "
+                "AND myothertable.othername = mytable_1.name",
+                dialect=mssql.dialect())
+
     def test_delete(self):
         self.assert_compile(
                         delete(table1, table1.c.myid == 7), 
index 8eccde999b33ff7f9dd2555ba3a9230bbce8a7f2..f900a164cf73cc6aebc2b2aa898a8124d7844f36 100644 (file)
@@ -156,6 +156,31 @@ class UpdateFromRoundTripTest(_UpdateFromTestBase, fixtures.TablesTest):
             ]
         )
 
+    @testing.requires.update_from
+    def test_exec_two_table_plus_alias(self):
+        users, addresses = self.tables.users, self.tables.addresses
+        a1 = addresses.alias()
+
+        testing.db.execute(
+            addresses.update().\
+                values(email_address=users.c.name).\
+                where(users.c.id==a1.c.user_id).\
+                where(users.c.name=='ed').\
+                where(a1.c.id==addresses.c.id)
+        )
+        eq_(
+            testing.db.execute(
+                addresses.select().\
+                    order_by(addresses.c.id)).fetchall(),
+            [
+                (1, 7, 'x', "jack@bean.com"),
+                (2, 8, 'x', "ed"),
+                (3, 8, 'x', "ed"),
+                (4, 8, 'x', "ed"),
+                (5, 9, 'x', "fred@fred.com")
+            ]
+        )
+
     @testing.requires.update_from
     def test_exec_three_table(self):
         users, addresses, dingalings = \