)
-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)
)
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)
rows = 0
records = list(records)
+
if hasvalue:
for state, state_dict, params, mapper, \
connection, value_params in records:
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
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,
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`
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.
: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
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)
run_define_tables = 'each'
-class BulkInsertTest(BulkTest, _fixtures.FixtureTest):
+class BulkInsertUpdateTest(BulkTest, _fixtures.FixtureTest):
@classmethod
def setup_mappers(cls):
)
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