Ticket #11156: 12037.diff

File 12037.diff, 13.1 KB (added by Richard Davies <richard.davies@…>, 6 years ago)
  • django/db/models/query.py

     
    364364                params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
    365365                params.update(defaults)
    366366                obj = self.model(**params)
    367                 sid = transaction.savepoint(using=self.db)
     367                sid = transaction.savepoint(using=self.db, only_if_needed=True)
    368368                obj.save(force_insert=True, using=self.db)
    369                 transaction.savepoint_commit(sid, using=self.db)
     369                transaction.savepoint_commit(sid, using=self.db,
     370                                             only_if_needed=True)
    370371                return obj, True
    371372            except IntegrityError, e:
    372                 transaction.savepoint_rollback(sid, using=self.db)
     373                transaction.savepoint_rollback(sid, using=self.db,
     374                                               only_if_needed=True)
    373375                try:
    374376                    return self.get(**kwargs), False
    375377                except self.model.DoesNotExist:
  • django/db/backends/postgresql/base.py

     
    6767
    6868class DatabaseFeatures(BaseDatabaseFeatures):
    6969    uses_savepoints = True
     70    needs_savepoint_on_exception = True
    7071
    7172class DatabaseWrapper(BaseDatabaseWrapper):
    7273    operators = {
     
    124125            if self._version[0:2] < (8, 0):
    125126                # No savepoint support for earlier version of PostgreSQL.
    126127                self.features.uses_savepoints = False
     128                # Transactions will just break on exceptions
     129                # and there's nothing we can do about it
     130                self.features.needs_savepoint_on_exception = False
    127131        cursor.execute("SET client_encoding to 'UNICODE'")
    128132        cursor = UnicodeCursorWrapper(cursor, 'utf-8')
    129133        return cursor
  • django/db/backends/__init__.py

     
    5050        """
    5151        pass
    5252
    53     def _savepoint(self, sid):
     53    def _savepoint(self, sid, only_if_needed=False):
    5454        if not self.features.uses_savepoints:
    5555            return
     56        if only_if_needed and not self.features.needs_savepoint_on_exception:
     57            return
    5658        self.cursor().execute(self.ops.savepoint_create_sql(sid))
    5759
    58     def _savepoint_rollback(self, sid):
     60    def _savepoint_rollback(self, sid, only_if_needed=False):
    5961        if not self.features.uses_savepoints:
    6062            return
     63        if only_if_needed and not self.features.needs_savepoint_on_exception:
     64            return
    6165        self.cursor().execute(self.ops.savepoint_rollback_sql(sid))
    6266
    63     def _savepoint_commit(self, sid):
     67    def _savepoint_commit(self, sid, only_if_needed=False):
    6468        if not self.features.uses_savepoints:
    6569            return
     70        if only_if_needed and not self.features.needs_savepoint_on_exception:
     71            return
    6672        self.cursor().execute(self.ops.savepoint_commit_sql(sid))
    6773
    6874    def close(self):
     
    9298    can_return_id_from_insert = False
    9399    uses_autocommit = False
    94100    uses_savepoints = False
     101    needs_savepoint_on_exception = False
    95102    # If True, don't use integer foreign keys referring to, e.g., positive
    96103    # integer primary keys.
    97104    related_fields_match_type = False
  • django/db/backends/postgresql_psycopg2/base.py

     
    107107            if self._version[0:2] < (8, 0):
    108108                # No savepoint support for earlier version of PostgreSQL.
    109109                self.features.uses_savepoints = False
     110                # Transactions will just break on exceptions
     111                # and there's nothing we can do about it
     112                self.features.needs_savepoint_on_exception = False
    110113            if self.features.uses_autocommit:
    111114                if self._version[0:2] < (8, 2):
    112115                    # FIXME: Needs extra code to do reliable model insert
     
    149152        finally:
    150153            self.isolation_level = level
    151154            self.features.uses_savepoints = bool(level)
     155            self.features.needs_savepoint_on_exception = bool(level)
  • django/db/transaction.py

     
    209209    connection._rollback()
    210210    set_clean(using=using)
    211211
    212 def savepoint(using=None):
     212def savepoint(using=None, only_if_needed=False):
    213213    """
    214214    Creates a savepoint (if supported and required by the backend) inside the
    215215    current transaction. Returns an identifier for the savepoint that will be
     
    226226        savepoint_state[thread_ident][using] = [None]
    227227    tid = str(thread_ident).replace('-', '')
    228228    sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident][using]))
    229     connection._savepoint(sid)
     229    connection._savepoint(sid, only_if_needed)
    230230    return sid
    231231
    232 def savepoint_rollback(sid, using=None):
     232def savepoint_rollback(sid, using=None, only_if_needed=False):
    233233    """
    234234    Rolls back the most recent savepoint (if one exists). Does nothing if
    235     savepoints are not supported.
     235    savepoints are not supported, or if only_if_needed is set and the backend
     236    does not need to use savepoints to recover from exceptions.
    236237    """
    237238    if using is None:
    238239        using = DEFAULT_DB_ALIAS
    239240    connection = connections[using]
    240241    thread_ident = thread.get_ident()
    241242    if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
    242         connection._savepoint_rollback(sid)
     243        connection._savepoint_rollback(sid, only_if_needed)
    243244
    244 def savepoint_commit(sid, using=None):
     245def savepoint_commit(sid, using=None, only_if_needed=False):
    245246    """
    246247    Commits the most recent savepoint (if one exists). Does nothing if
    247     savepoints are not supported.
     248    savepoints are not supported, or if only_if_needed is set and the backend
     249    does not need to use savepoints to recover from exceptions.
    248250    """
    249251    if using is None:
    250252        using = DEFAULT_DB_ALIAS
    251253    connection = connections[using]
    252254    thread_ident = thread.get_ident()
    253255    if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
    254         connection._savepoint_commit(sid)
     256        connection._savepoint_commit(sid, only_if_needed)
    255257
    256258##############
    257259# DECORATORS #
  • django/contrib/sessions/backends/db.py

     
    5858            session_data = self.encode(self._get_session(no_load=must_create)),
    5959            expire_date = self.get_expiry_date()
    6060        )
    61         sid = transaction.savepoint(using=self.using)
     61        sid = transaction.savepoint(using=self.using, only_if_needed=True)
    6262        try:
    6363            obj.save(force_insert=must_create)
    6464        except IntegrityError:
    6565            if must_create:
    66                 transaction.savepoint_rollback(sid, using=self.using)
     66                transaction.savepoint_rollback(sid, using=self.using,
     67                                               only_if_needed=True)
    6768                raise CreateError
    6869            raise
    6970
  • tests/modeltests/force_insert_update/models.py

     
    4040>>> c1.save(force_insert=True)
    4141
    4242# Won't work because we can't insert a pk of the same value.
    43 >>> sid = transaction.savepoint()
     43>>> sid = transaction.savepoint(only_if_needed=True)
    4444>>> c.value = 5
    4545>>> try:
    4646...     c.save(force_insert=True)
     
    5050...     else:
    5151...         print "Fail with %s" % type(e)
    5252Pass
    53 >>> transaction.savepoint_rollback(sid)
     53>>> transaction.savepoint_rollback(sid, only_if_needed=True)
    5454
    5555# Trying to update should still fail, even with manual primary keys, if the
    5656# data isn't in the database already.
  • tests/modeltests/one_to_one/models.py

     
    180180
    181181# This will fail because each one-to-one field must be unique (and link2=o1 was
    182182# used for x1, above).
    183 >>> sid = transaction.savepoint()
     183>>> sid = transaction.savepoint(only_if_needed=True)
    184184>>> try:
    185185...     MultiModel(link1=p2, link2=o1, name="x1").save()
    186186... except Exception, e:
     
    189189...     else:
    190190...         print "Fail with %s" % type(e)
    191191Pass
    192 >>> transaction.savepoint_rollback(sid)
     192>>> transaction.savepoint_rollback(sid, only_if_needed=True)
    193193
    194194"""}
  • tests/modeltests/custom_pk/models.py

     
    122122# The primary key must also obviously be unique, so trying to create a new
    123123# object with the same primary key will fail.
    124124>>> try:
    125 ...    sid = transaction.savepoint()
     125...    sid = transaction.savepoint(only_if_needed=True)
    126126...    Employee.objects.create(employee_code=123, first_name='Fred', last_name='Jones')
    127 ...    transaction.savepoint_commit(sid)
     127...    transaction.savepoint_commit(sid, only_if_needed=True)
    128128... except Exception, e:
    129129...    if isinstance(e, IntegrityError):
    130 ...        transaction.savepoint_rollback(sid)
     130...        transaction.savepoint_rollback(sid, only_if_needed=True)
    131131...        print "Pass"
    132132...    else:
    133133...        print "Fail with %s" % type(e)
     
    161161# The primary key must be specified, so an error is raised if you try to create
    162162# an object without it.
    163163>>> try:
    164 ...     sid = transaction.savepoint()
     164...     sid = transaction.savepoint(only_if_needed=True)
    165165...     Employee.objects.create(first_name='Tom', last_name='Smith')
    166166...     print 'hello'
    167 ...     transaction.savepoint_commit(sid)
     167...     transaction.savepoint_commit(sid, only_if_needed=True)
    168168...     print 'hello2'
    169169... except Exception, e:
    170170...     if isinstance(e, IntegrityError):
    171 ...         transaction.savepoint_rollback(sid)
     171...         transaction.savepoint_rollback(sid, only_if_needed=True)
    172172...         print "Pass"
    173173...     else:
    174174...         print "Fail with %s" % type(e)
  • docs/topics/db/transactions.txt

     
    191191
    192192Savepoints are controlled by three methods on the transaction object:
    193193
    194 .. method:: transaction.savepoint(using=None)
     194.. method:: transaction.savepoint(using=None, only_if_needed=False)
    195195
    196196    Creates a new savepoint. This marks a point in the transaction that
    197197    is known to be in a "good" state.
    198198
    199199    Returns the savepoint ID (sid).
    200200
    201 .. method:: transaction.savepoint_commit(sid, using=None)
     201.. method:: transaction.savepoint_commit(sid, using=None, only_if_needed=False)
    202202
    203203    Updates the savepoint to include any operations that have been performed
    204204    since the savepoint was created, or since the last commit.
    205205
    206 .. method:: transaction.savepoint_rollback(sid, using=None)
     206.. method:: transaction.savepoint_rollback(sid, using=None, only_if_needed=False)
    207207
    208208    Rolls the transaction back to the last point at which the savepoint was
    209209    committed.
    210210
     211.. note::
     212
     213    The ``only_if_needed`` flag is an advanced option, which is used in the
     214    context described under "Handling exceptions within PostgreSQL
     215    transactions", below.
     216
    211217The following example demonstrates the use of savepoints::
    212218
    213219    from django.db import transaction
     
    288294
    289295    a.save() # Succeeds, and never undone by savepoint rollback
    290296    try:
    291         sid = transaction.savepoint()
     297        sid = transaction.savepoint(only_if_needed=True)
    292298        b.save() # Could throw exception
    293         transaction.savepoint_commit(sid)
     299        transaction.savepoint_commit(sid, only_if_needed=True)
    294300    except IntegrityError:
    295         transaction.savepoint_rollback(sid)
     301        transaction.savepoint_rollback(sid, only_if_needed=True)
    296302    c.save() # Succeeds, and a.save() is never undone
    297303
    298304In this example, ``a.save()`` will not be undone in the case where
    299 ``b.save()`` raises an exception.
     305``b.save()`` raises an exception. The ``only_if_needed`` flag means that the
     306savepoint code will only be executed on databases such as PostgreSQL which
     307require it to recover from exceptions, not on other databases.
    300308
    301309Database-level autocommit
    302310-------------------------
Back to Top