]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- add an option for bulk_save -> update to not do history
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 8 Dec 2014 01:36:01 +0000 (20:36 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 8 Dec 2014 01:36:01 +0000 (20:36 -0500)
lib/sqlalchemy/orm/persistence.py
lib/sqlalchemy/orm/session.py
test/orm/test_bulk.py

index d94fbb040dc12734f0509ea86622829b19294c0e..f477e1dd7becb87b329115dbfd04640bcaab07e2 100644 (file)
@@ -75,7 +75,8 @@ def _bulk_insert(
             )
 
 
-def _bulk_update(mapper, mappings, session_transaction, isstates):
+def _bulk_update(mapper, mappings, session_transaction,
+                 isstates, update_changed_only):
     base_mapper = mapper.base_mapper
 
     cached_connections = _cached_connection_dict(base_mapper)
@@ -88,7 +89,10 @@ def _bulk_update(mapper, mappings, session_transaction, isstates):
         )
 
     if isstates:
-        mappings = [_changed_dict(mapper, state) for state in mappings]
+        if update_changed_only:
+            mappings = [_changed_dict(mapper, state) for state in mappings]
+        else:
+            mappings = [state.dict for state in mappings]
     else:
         mappings = list(mappings)
 
@@ -612,6 +616,7 @@ def _emit_update_statements(base_mapper, uowtransaction,
 
         rows = 0
         records = list(records)
+
         if hasvalue:
             for state, state_dict, params, mapper, \
                     connection, value_params in records:
index e07b4554e875295760ce937badac243f90cf3d2a..72d393f54b9554d95b63a8fcdc021579524ee560 100644 (file)
@@ -2047,7 +2047,8 @@ class Session(_SessionClassMethods):
             with util.safe_reraise():
                 transaction.rollback(_capture_exception=True)
 
-    def bulk_save_objects(self, objects, return_defaults=False):
+    def bulk_save_objects(
+            self, objects, return_defaults=False, update_changed_only=True):
         """Perform a bulk save of the given list of objects.
 
         The bulk save feature allows mapped objects to be used as the
@@ -2083,12 +2084,13 @@ class Session(_SessionClassMethods):
          attribute set, then the object is assumed to be "detached" and
          will result in an UPDATE.  Otherwise, an INSERT is used.
 
-         In the case of an UPDATE, **all** those attributes which are present
-         and are not part of the primary key are applied to the SET clause
-         of the UPDATE statement, regardless of whether any change in state
-         was logged on each attribute; there is no checking of per-attribute
-         history.  The primary key attributes, which are required,
-         are applied to the WHERE clause.
+         In the case of an UPDATE, statements are grouped based on which
+         attributes have changed, and are thus to be the subject of each
+         SET clause.  If ``update_changed_only`` is False, then all
+         attributes present within each object are applied to the UPDATE
+         statement, which may help in allowing the statements to be grouped
+         together into a larger executemany(), and will also reduce the
+         overhead of checking history on attributes.
 
         :param return_defaults: when True, rows that are missing values which
          generate defaults, namely integer primary key defaults and sequences,
@@ -2099,6 +2101,11 @@ class Session(_SessionClassMethods):
          return_defaults mode greatly reduces the performance gains of the
          method overall.
 
+        :param update_changed_only: when True, UPDATE statements are rendered
+         based on those attributes in each state that have logged changes.
+         When False, all attributes present are rendered into the SET clause
+         with the exception of primary key attributes.
+
         .. seealso::
 
             :ref:`bulk_operations`
@@ -2113,7 +2120,8 @@ class Session(_SessionClassMethods):
             lambda state: (state.mapper, state.key is not None)
         ):
             self._bulk_save_mappings(
-                mapper, states, isupdate, True, return_defaults)
+                mapper, states, isupdate, True,
+                return_defaults, update_changed_only)
 
     def bulk_insert_mappings(self, mapper, mappings, return_defaults=False):
         """Perform a bulk insert of the given list of mapping dictionaries.
@@ -2218,10 +2226,11 @@ class Session(_SessionClassMethods):
             :meth:`.Session.bulk_save_objects`
 
         """
-        self._bulk_save_mappings(mapper, mappings, True, False, False)
+        self._bulk_save_mappings(mapper, mappings, True, False, False, False)
 
     def _bulk_save_mappings(
-            self, mapper, mappings, isupdate, isstates, return_defaults):
+            self, mapper, mappings, isupdate, isstates,
+            return_defaults, update_changed_only):
         mapper = _class_to_mapper(mapper)
         self._flushing = True
 
@@ -2230,7 +2239,8 @@ class Session(_SessionClassMethods):
         try:
             if isupdate:
                 persistence._bulk_update(
-                    mapper, mappings, transaction, isstates)
+                    mapper, mappings, transaction,
+                    isstates, update_changed_only)
             else:
                 persistence._bulk_insert(
                     mapper, mappings, transaction, isstates, return_defaults)
index f6d2513d18f3d4280aba7c6be837992d233261e8..e27d3b73c84f30eac05ba505843bb9a0ff543f86 100644 (file)
@@ -13,7 +13,7 @@ class BulkTest(testing.AssertsExecutionResults):
     run_define_tables = 'each'
 
 
-class BulkInsertTest(BulkTest, _fixtures.FixtureTest):
+class BulkInsertUpdateTest(BulkTest, _fixtures.FixtureTest):
 
     @classmethod
     def setup_mappers(cls):
@@ -75,6 +75,35 @@ class BulkInsertTest(BulkTest, _fixtures.FixtureTest):
         )
         assert 'id' not in objects[0].__dict__
 
+    def test_bulk_save_updated_include_unchanged(self):
+        User, = self.classes("User",)
+
+        s = Session(expire_on_commit=False)
+        objects = [
+            User(name="u1"),
+            User(name="u2"),
+            User(name="u3")
+        ]
+        s.add_all(objects)
+        s.commit()
+
+        objects[0].name = 'u1new'
+        objects[2].name = 'u3new'
+
+        s = Session()
+        with self.sql_execution_asserter() as asserter:
+            s.bulk_save_objects(objects, update_changed_only=False)
+
+        asserter.assert_(
+            CompiledSQL(
+                "UPDATE users SET id=:id, name=:name WHERE "
+                "users.id = :users_id",
+                [{'users_id': 1, 'id': 1, 'name': 'u1new'},
+                 {'users_id': 2, 'id': 2, 'name': 'u2'},
+                 {'users_id': 3, 'id': 3, 'name': 'u3new'}]
+            )
+        )
+
 
 class BulkInheritanceTest(BulkTest, fixtures.MappedTest):
     @classmethod