From 191fd3e27e3ef90190f8315c33ba6eb97aeaf5d2 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 14 Aug 2014 15:38:30 -0400 Subject: - proof of concept --- lib/sqlalchemy/orm/session.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 036045dba..2455c803a 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -2033,6 +2033,40 @@ class Session(_SessionClassMethods): with util.safe_reraise(): transaction.rollback(_capture_exception=True) + def bulk_save(self, objects): + self._flushing = True + flush_context = UOWTransaction(self) + + if self.dispatch.before_bulk_save: + self.dispatch.before_bulk_save( + self, flush_context, objects) + + flush_context.transaction = transaction = self.begin( + subtransactions=True) + try: + self._warn_on_events = True + try: + flush_context.bulk_save(objects) + finally: + self._warn_on_events = False + + self.dispatch.after_bulk_save( + self, flush_context, objects + ) + + flush_context.finalize_flush_changes() + + self.dispatch.after_bulk_save_postexec( + self, flush_context, objects) + + transaction.commit() + + except: + with util.safe_reraise(): + transaction.rollback(_capture_exception=True) + finally: + self._flushing = False + def is_modified(self, instance, include_collections=True, passive=True): """Return ``True`` if the given instance has locally -- cgit v1.2.1 From 6bc676f56d57d5ea4dc298f63d0e3a77c0f4a4a1 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 14 Aug 2014 17:44:58 -0400 Subject: dev --- lib/sqlalchemy/orm/session.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 2455c803a..546355611 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -482,7 +482,7 @@ class Session(_SessionClassMethods): '__contains__', '__iter__', 'add', 'add_all', 'begin', 'begin_nested', 'close', 'commit', 'connection', 'delete', 'execute', 'expire', 'expire_all', 'expunge', 'expunge_all', 'flush', 'get_bind', - 'is_modified', + 'is_modified', 'bulk_save_objects', 'bulk_save_mappings', 'merge', 'query', 'refresh', 'rollback', 'scalar') @@ -2033,31 +2033,42 @@ class Session(_SessionClassMethods): with util.safe_reraise(): transaction.rollback(_capture_exception=True) - def bulk_save(self, objects): + def bulk_save_objects(self, objects): + self._bulk_save((attributes.instance_state(obj) for obj in objects)) + + def bulk_save_mappings(self, mapper, mappings): + mapper = class_mapper(mapper) + + self._bulk_save(( + statelib.MappingState(mapper, mapping) + for mapping in mappings) + ) + + def _bulk_save(self, states): self._flushing = True flush_context = UOWTransaction(self) if self.dispatch.before_bulk_save: self.dispatch.before_bulk_save( - self, flush_context, objects) + self, flush_context, states) flush_context.transaction = transaction = self.begin( subtransactions=True) try: self._warn_on_events = True try: - flush_context.bulk_save(objects) + flush_context.bulk_save(states) finally: self._warn_on_events = False self.dispatch.after_bulk_save( - self, flush_context, objects + self, flush_context, states ) flush_context.finalize_flush_changes() self.dispatch.after_bulk_save_postexec( - self, flush_context, objects) + self, flush_context, states) transaction.commit() -- cgit v1.2.1 From 591f2e4ed2d455cb2c5b9ece43d79fde4b109510 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 14 Aug 2014 19:47:23 -0400 Subject: - change to be represented as two very fast bulk_insert() and bulk_update() methods --- lib/sqlalchemy/orm/session.py | 57 ++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 28 deletions(-) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 546355611..3199a4332 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -20,6 +20,7 @@ from .base import ( _class_to_mapper, _state_mapper, object_state, _none_set, state_str, instance_str ) +import itertools from .unitofwork import UOWTransaction from . import state as statelib import sys @@ -482,7 +483,8 @@ class Session(_SessionClassMethods): '__contains__', '__iter__', 'add', 'add_all', 'begin', 'begin_nested', 'close', 'commit', 'connection', 'delete', 'execute', 'expire', 'expire_all', 'expunge', 'expunge_all', 'flush', 'get_bind', - 'is_modified', 'bulk_save_objects', 'bulk_save_mappings', + 'is_modified', 'bulk_save_objects', 'bulk_insert_mappings', + 'bulk_update_mappings', 'merge', 'query', 'refresh', 'rollback', 'scalar') @@ -2034,42 +2036,41 @@ class Session(_SessionClassMethods): transaction.rollback(_capture_exception=True) def bulk_save_objects(self, objects): - self._bulk_save((attributes.instance_state(obj) for obj in objects)) + for (mapper, isupdate), states in itertools.groupby( + (attributes.instance_state(obj) for obj in objects), + lambda state: (state.mapper, state.key is not None) + ): + if isupdate: + self.bulk_update_mappings(mapper, (s.dict for s in states)) + else: + self.bulk_insert_mappings(mapper, (s.dict for s in states)) - def bulk_save_mappings(self, mapper, mappings): - mapper = class_mapper(mapper) + def bulk_insert_mappings(self, mapper, mappings): + self._bulk_save_mappings(mapper, mappings, False) - self._bulk_save(( - statelib.MappingState(mapper, mapping) - for mapping in mappings) - ) + def bulk_update_mappings(self, mapper, mappings): + self._bulk_save_mappings(mapper, mappings, True) - def _bulk_save(self, states): + def _bulk_save_mappings(self, mapper, mappings, isupdate): + mapper = _class_to_mapper(mapper) self._flushing = True flush_context = UOWTransaction(self) - if self.dispatch.before_bulk_save: - self.dispatch.before_bulk_save( - self, flush_context, states) - flush_context.transaction = transaction = self.begin( subtransactions=True) try: - self._warn_on_events = True - try: - flush_context.bulk_save(states) - finally: - self._warn_on_events = False - - self.dispatch.after_bulk_save( - self, flush_context, states - ) - - flush_context.finalize_flush_changes() - - self.dispatch.after_bulk_save_postexec( - self, flush_context, states) - + if isupdate: + self.dispatch.before_bulk_update( + self, flush_context, mapper, mappings) + flush_context.bulk_update(mapper, mappings) + self.dispatch.after_bulk_update( + self, flush_context, mapper, mappings) + else: + self.dispatch.before_bulk_insert( + self, flush_context, mapper, mappings) + flush_context.bulk_insert(mapper, mappings) + self.dispatch.after_bulk_insert( + self, flush_context, mapper, mappings) transaction.commit() except: -- cgit v1.2.1 From 91959122e0a12943e5ff9399024c65ad4d7489e1 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 19 Aug 2014 14:24:56 -0400 Subject: - refinements --- lib/sqlalchemy/orm/session.py | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 3199a4332..968868e84 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -21,6 +21,7 @@ from .base import ( _none_set, state_str, instance_str ) import itertools +from . import persistence from .unitofwork import UOWTransaction from . import state as statelib import sys @@ -2040,37 +2041,27 @@ class Session(_SessionClassMethods): (attributes.instance_state(obj) for obj in objects), lambda state: (state.mapper, state.key is not None) ): - if isupdate: - self.bulk_update_mappings(mapper, (s.dict for s in states)) - else: - self.bulk_insert_mappings(mapper, (s.dict for s in states)) + self._bulk_save_mappings(mapper, states, isupdate, True) def bulk_insert_mappings(self, mapper, mappings): - self._bulk_save_mappings(mapper, mappings, False) + self._bulk_save_mappings(mapper, mappings, False, False) def bulk_update_mappings(self, mapper, mappings): - self._bulk_save_mappings(mapper, mappings, True) + self._bulk_save_mappings(mapper, mappings, True, False) - def _bulk_save_mappings(self, mapper, mappings, isupdate): + def _bulk_save_mappings(self, mapper, mappings, isupdate, isstates): mapper = _class_to_mapper(mapper) self._flushing = True - flush_context = UOWTransaction(self) - flush_context.transaction = transaction = self.begin( + transaction = self.begin( subtransactions=True) try: if isupdate: - self.dispatch.before_bulk_update( - self, flush_context, mapper, mappings) - flush_context.bulk_update(mapper, mappings) - self.dispatch.after_bulk_update( - self, flush_context, mapper, mappings) + persistence._bulk_update( + mapper, mappings, transaction, isstates) else: - self.dispatch.before_bulk_insert( - self, flush_context, mapper, mappings) - flush_context.bulk_insert(mapper, mappings) - self.dispatch.after_bulk_insert( - self, flush_context, mapper, mappings) + persistence._bulk_insert( + mapper, mappings, transaction, isstates) transaction.commit() except: -- cgit v1.2.1 From ccfd26d96916cc7953f1fefa8abed53d4f696c4c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 2 Sep 2014 19:23:09 -0400 Subject: - add options to get back pk defaults for inserts. times spent start getting barely different... --- lib/sqlalchemy/orm/session.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index e075b9c71..1611688b0 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -2036,20 +2036,22 @@ class Session(_SessionClassMethods): with util.safe_reraise(): transaction.rollback(_capture_exception=True) - def bulk_save_objects(self, objects): + def bulk_save_objects(self, objects, return_defaults=False): for (mapper, isupdate), states in itertools.groupby( (attributes.instance_state(obj) for obj in objects), lambda state: (state.mapper, state.key is not None) ): - self._bulk_save_mappings(mapper, states, isupdate, True) + self._bulk_save_mappings( + mapper, states, isupdate, True, return_defaults) - def bulk_insert_mappings(self, mapper, mappings): - self._bulk_save_mappings(mapper, mappings, False, False) + def bulk_insert_mappings(self, mapper, mappings, return_defaults=False): + self._bulk_save_mappings(mapper, mappings, False, False, return_defaults) def bulk_update_mappings(self, mapper, mappings): - self._bulk_save_mappings(mapper, mappings, True, False) + self._bulk_save_mappings(mapper, mappings, True, False, False) - def _bulk_save_mappings(self, mapper, mappings, isupdate, isstates): + def _bulk_save_mappings( + self, mapper, mappings, isupdate, isstates, return_defaults): mapper = _class_to_mapper(mapper) self._flushing = True @@ -2061,7 +2063,7 @@ class Session(_SessionClassMethods): mapper, mappings, transaction, isstates) else: persistence._bulk_insert( - mapper, mappings, transaction, isstates) + mapper, mappings, transaction, isstates, return_defaults) transaction.commit() except: -- cgit v1.2.1 From e257ca6c5268517ec2e9a561372d82dfc10475e8 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 7 Dec 2014 18:55:23 -0500 Subject: - initial tests for bulk --- lib/sqlalchemy/orm/session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index ef911824c..7dd577230 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -2056,7 +2056,8 @@ class Session(_SessionClassMethods): mapper, states, isupdate, True, return_defaults) def bulk_insert_mappings(self, mapper, mappings, return_defaults=False): - self._bulk_save_mappings(mapper, mappings, False, False, return_defaults) + self._bulk_save_mappings( + mapper, mappings, False, False, return_defaults) def bulk_update_mappings(self, mapper, mappings): self._bulk_save_mappings(mapper, mappings, True, False, False) -- cgit v1.2.1 From c42b8f8eb8f4c324e2469bf3baaa316c214abce5 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 7 Dec 2014 20:21:20 -0500 Subject: - fix inheritance persistence - start writing docs --- lib/sqlalchemy/orm/session.py | 158 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 7dd577230..e07b4554e 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -2048,6 +2048,66 @@ class Session(_SessionClassMethods): transaction.rollback(_capture_exception=True) def bulk_save_objects(self, objects, return_defaults=False): + """Perform a bulk save of the given list of objects. + + The bulk save feature allows mapped objects to be used as the + source of simple INSERT and UPDATE operations which can be more easily + grouped together into higher performing "executemany" + operations; the extraction of data from the objects is also performed + using a lower-latency process that ignores whether or not attributes + have actually been modified in the case of UPDATEs, and also ignores + SQL expressions. + + The objects as given are not added to the session and no additional + state is established on them, unless the ``return_defaults`` flag + is also set. + + .. warning:: + + The bulk save feature allows for a lower-latency INSERT/UPDATE + of rows at the expense of a lack of features. Features such + as object management, relationship handling, and SQL clause + support are bypassed in favor of raw INSERT/UPDATES of records. + + **Please read the list of caveats at :ref:`bulk_operations` + before using this method.** + + :param objects: a list of mapped object instances. The mapped + objects are persisted as is, and are **not** associated with the + :class:`.Session` afterwards. + + For each object, whether the object is sent as an INSERT or an + UPDATE is dependent on the same rules used by the :class:`.Session` + in traditional operation; if the object has the + :attr:`.InstanceState.key` + 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. + + :param return_defaults: when True, rows that are missing values which + generate defaults, namely integer primary key defaults and sequences, + will be inserted **one at a time**, so that the primary key value + is available. In particular this will allow joined-inheritance + and other multi-table mappings to insert correctly without the need + to provide primary key values ahead of time; however, + return_defaults mode greatly reduces the performance gains of the + method overall. + + .. seealso:: + + :ref:`bulk_operations` + + :meth:`.Session.bulk_insert_mappings` + + :meth:`.Session.bulk_update_mappings` + + """ for (mapper, isupdate), states in itertools.groupby( (attributes.instance_state(obj) for obj in objects), lambda state: (state.mapper, state.key is not None) @@ -2056,10 +2116,108 @@ class Session(_SessionClassMethods): mapper, states, isupdate, True, return_defaults) def bulk_insert_mappings(self, mapper, mappings, return_defaults=False): + """Perform a bulk insert of the given list of mapping dictionaries. + + The bulk insert feature allows plain Python dictionaries to be used as + the source of simple INSERT operations which can be more easily + grouped together into higher performing "executemany" + operations. Using dictionaries, there is no "history" or session + state management features in use, reducing latency when inserting + large numbers of simple rows. + + The values within the dictionaries as given are typically passed + without modification into Core :meth:`.Insert` constructs, after + organizing the values within them across the tables to which + the given mapper is mapped. + + .. warning:: + + The bulk insert feature allows for a lower-latency INSERT + of rows at the expense of a lack of features. Features such + as relationship handling and SQL clause support are bypassed + in favor of a raw INSERT of records. + + **Please read the list of caveats at :ref:`bulk_operations` + before using this method.** + + :param mapper: a mapped class, or the actual :class:`.Mapper` object, + representing the single kind of object represented within the mapping + list. + + :param mappings: a list of dictionaries, each one containing the state + of the mapped row to be inserted, in terms of the attribute names + on the mapped class. If the mapping refers to multiple tables, + such as a joined-inheritance mapping, each dictionary must contain + all keys to be populated into all tables. + + :param return_defaults: when True, rows that are missing values which + generate defaults, namely integer primary key defaults and sequences, + will be inserted **one at a time**, so that the primary key value + is available. In particular this will allow joined-inheritance + and other multi-table mappings to insert correctly without the need + to provide primary + key values ahead of time; however, return_defaults mode greatly + reduces the performance gains of the method overall. If the rows + to be inserted only refer to a single table, then there is no + reason this flag should be set as the returned default information + is not used. + + + .. seealso:: + + :ref:`bulk_operations` + + :meth:`.Session.bulk_save_objects` + + :meth:`.Session.bulk_update_mappings` + + """ self._bulk_save_mappings( mapper, mappings, False, False, return_defaults) def bulk_update_mappings(self, mapper, mappings): + """Perform a bulk update of the given list of mapping dictionaries. + + The bulk update feature allows plain Python dictionaries to be used as + the source of simple UPDATE operations which can be more easily + grouped together into higher performing "executemany" + operations. Using dictionaries, there is no "history" or session + state management features in use, reducing latency when updating + large numbers of simple rows. + + .. warning:: + + The bulk update feature allows for a lower-latency UPDATE + of rows at the expense of a lack of features. Features such + as relationship handling and SQL clause support are bypassed + in favor of a raw UPDATE of records. + + **Please read the list of caveats at :ref:`bulk_operations` + before using this method.** + + :param mapper: a mapped class, or the actual :class:`.Mapper` object, + representing the single kind of object represented within the mapping + list. + + :param mappings: a list of dictionaries, each one containing the state + of the mapped row to be updated, in terms of the attribute names + on the mapped class. If the mapping refers to multiple tables, + such as a joined-inheritance mapping, each dictionary may contain + keys corresponding to all tables. All those keys which are present + and are not part of the primary key are applied to the SET clause + of the UPDATE statement; the primary key values, which are required, + are applied to the WHERE clause. + + + .. seealso:: + + :ref:`bulk_operations` + + :meth:`.Session.bulk_insert_mappings` + + :meth:`.Session.bulk_save_objects` + + """ self._bulk_save_mappings(mapper, mappings, True, False, False) def _bulk_save_mappings( -- cgit v1.2.1 From 07cc9e054ae4d5bb9cfc3c1d807b2a0d58a95b69 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 7 Dec 2014 20:36:01 -0500 Subject: - add an option for bulk_save -> update to not do history --- lib/sqlalchemy/orm/session.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index e07b4554e..72d393f54 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -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) -- cgit v1.2.1 From 3f1477e2ecf3b2e95a26383490d0e8c363f4d0cc Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 8 Dec 2014 01:10:30 -0500 Subject: - A new series of :class:`.Session` methods which provide hooks directly into the unit of work's facility for emitting INSERT and UPDATE statements has been created. When used correctly, this expert-oriented system can allow ORM-mappings to be used to generate bulk insert and update statements batched into executemany groups, allowing the statements to proceed at speeds that rival direct use of the Core. fixes #3100 --- lib/sqlalchemy/orm/session.py | 59 +++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 22 deletions(-) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 72d393f54..d40d28154 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -2061,17 +2061,22 @@ class Session(_SessionClassMethods): The objects as given are not added to the session and no additional state is established on them, unless the ``return_defaults`` flag - is also set. + is also set, in which case primary key attributes and server-side + default values will be populated. + + .. versionadded:: 1.0.0 .. warning:: The bulk save feature allows for a lower-latency INSERT/UPDATE - of rows at the expense of a lack of features. Features such - as object management, relationship handling, and SQL clause - support are bypassed in favor of raw INSERT/UPDATES of records. + of rows at the expense of most other unit-of-work features. + Features such as object management, relationship handling, + and SQL clause support are **silently omitted** in favor of raw + INSERT/UPDATES of records. - **Please read the list of caveats at :ref:`bulk_operations` - before using this method.** + **Please read the list of caveats at** :ref:`bulk_operations` + **before using this method, and fully test and confirm the + functionality of all code developed using these systems.** :param objects: a list of mapped object instances. The mapped objects are persisted as is, and are **not** associated with the @@ -2098,8 +2103,8 @@ class Session(_SessionClassMethods): is available. In particular this will allow joined-inheritance and other multi-table mappings to insert correctly without the need to provide primary key values ahead of time; however, - return_defaults mode greatly reduces the performance gains of the - method overall. + :paramref:`.Session.bulk_save_objects.return_defaults` **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. @@ -2138,15 +2143,19 @@ class Session(_SessionClassMethods): organizing the values within them across the tables to which the given mapper is mapped. + .. versionadded:: 1.0.0 + .. warning:: The bulk insert feature allows for a lower-latency INSERT - of rows at the expense of a lack of features. Features such - as relationship handling and SQL clause support are bypassed - in favor of a raw INSERT of records. + of rows at the expense of most other unit-of-work features. + Features such as object management, relationship handling, + and SQL clause support are **silently omitted** in favor of raw + INSERT of records. - **Please read the list of caveats at :ref:`bulk_operations` - before using this method.** + **Please read the list of caveats at** :ref:`bulk_operations` + **before using this method, and fully test and confirm the + functionality of all code developed using these systems.** :param mapper: a mapped class, or the actual :class:`.Mapper` object, representing the single kind of object represented within the mapping @@ -2164,8 +2173,10 @@ class Session(_SessionClassMethods): is available. In particular this will allow joined-inheritance and other multi-table mappings to insert correctly without the need to provide primary - key values ahead of time; however, return_defaults mode greatly - reduces the performance gains of the method overall. If the rows + key values ahead of time; however, + :paramref:`.Session.bulk_insert_mappings.return_defaults` + **greatly reduces the performance gains** of the method overall. + If the rows to be inserted only refer to a single table, then there is no reason this flag should be set as the returned default information is not used. @@ -2181,7 +2192,7 @@ class Session(_SessionClassMethods): """ self._bulk_save_mappings( - mapper, mappings, False, False, return_defaults) + mapper, mappings, False, False, return_defaults, False) def bulk_update_mappings(self, mapper, mappings): """Perform a bulk update of the given list of mapping dictionaries. @@ -2193,15 +2204,19 @@ class Session(_SessionClassMethods): state management features in use, reducing latency when updating large numbers of simple rows. + .. versionadded:: 1.0.0 + .. warning:: The bulk update feature allows for a lower-latency UPDATE - of rows at the expense of a lack of features. Features such - as relationship handling and SQL clause support are bypassed - in favor of a raw UPDATE of records. - - **Please read the list of caveats at :ref:`bulk_operations` - before using this method.** + of rows at the expense of most other unit-of-work features. + Features such as object management, relationship handling, + and SQL clause support are **silently omitted** in favor of raw + UPDATES of records. + + **Please read the list of caveats at** :ref:`bulk_operations` + **before using this method, and fully test and confirm the + functionality of all code developed using these systems.** :param mapper: a mapped class, or the actual :class:`.Mapper` object, representing the single kind of object represented within the mapping -- cgit v1.2.1 From cf7981f60d485f17465f44c6ff651ae283ade377 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 12 Dec 2014 19:59:11 -0500 Subject: - Added new method :meth:`.Session.invalidate`, functions similarly to :meth:`.Session.close`, except also calls :meth:`.Connection.invalidate` on all connections, guaranteeing that they will not be returned to the connection pool. This is useful in situations e.g. dealing with gevent timeouts when it is not safe to use the connection further, even for rollbacks. references #3258 --- lib/sqlalchemy/orm/session.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index d40d28154..507e99b2e 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -435,11 +435,13 @@ class SessionTransaction(object): self.session.dispatch.after_rollback(self.session) - def close(self): + def close(self, invalidate=False): self.session.transaction = self._parent if self._parent is None: for connection, transaction, autoclose in \ set(self._connections.values()): + if invalidate: + connection.invalidate() if autoclose: connection.close() else: @@ -1000,10 +1002,46 @@ class Session(_SessionClassMethods): not use any connection resources until they are first needed. """ + self._close_impl(invalidate=False) + + def invalidate(self): + """Close this Session, using connection invalidation. + + This is a variant of :meth:`.Session.close` that will additionally + ensure that the :meth:`.Connection.invalidate` method will be called + on all :class:`.Connection` objects. This can be called when + the database is known to be in a state where the connections are + no longer safe to be used. + + E.g.:: + + try: + sess = Session() + sess.add(User()) + sess.commit() + except gevent.Timeout: + sess.invalidate() + raise + except: + sess.rollback() + raise + + This clears all items and ends any transaction in progress. + + If this session were created with ``autocommit=False``, a new + transaction is immediately begun. Note that this new transaction does + not use any connection resources until they are first needed. + + .. versionadded:: 0.9.9 + + """ + self._close_impl(invalidate=True) + + def _close_impl(self, invalidate): self.expunge_all() if self.transaction is not None: for transaction in self.transaction._iterate_parents(): - transaction.close() + transaction.close(invalidate) def expunge_all(self): """Remove all object instances from this ``Session``. -- cgit v1.2.1 From 544e72bcb6af1ca657b1762f105634372eca3bc0 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 27 Dec 2014 15:55:30 -0500 Subject: - corrections - attempt to add a script to semi-automate the fixing of links --- lib/sqlalchemy/orm/session.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/sqlalchemy/orm/session.py') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 507e99b2e..0e272dc95 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -596,8 +596,8 @@ class Session(_SessionClassMethods): .. versionadded:: 0.9.0 :param query_cls: Class which should be used to create new Query - objects, as returned by the :meth:`~.Session.query` method. - Defaults to :class:`.Query`. + objects, as returned by the :meth:`~.Session.query` method. + Defaults to :class:`.Query`. :param twophase: When ``True``, all transactions will be started as a "two phase" transaction, i.e. using the "two phase" semantics -- cgit v1.2.1