Django

Code

Ticket #3460: 3640_r9736-autocommit-safe.diff

File 3640_r9736-autocommit-safe.diff, 8.0 kB (added by iamseb, 1 year ago)
  • django/db/models/sql/subqueries.py

    old new  
    304304        result = ['INSERT INTO %s' % qn(self.model._meta.db_table)] 
    305305        result.append('(%s)' % ', '.join([qn(c) for c in self.columns])) 
    306306        result.append('VALUES (%s)' % ', '.join(self.values)) 
     307        if self.connection.features.can_return_id_from_insert: 
     308            result.append('RETURNING %s.%s' % (qn(self.model._meta.db_table),  
     309                                 qn(self.model._meta.pk.column))) 
     310 
    307311        return ' '.join(result), self.params 
    308312 
    309313    def execute_sql(self, return_id=False): 
    310314        cursor = super(InsertQuery, self).execute_sql(None) 
    311         if return_id: 
     315        if return_id and self.connection.features.can_return_id_from_insert: 
     316            row_id = cursor.fetchone()[0] 
     317            return row_id 
     318        elif return_id: 
    312319            return self.connection.ops.last_insert_id(cursor, 
    313320                    self.model._meta.db_table, self.model._meta.pk.column) 
    314321 
  • django/db/backends/__init__.py

    old new  
    3131        if self.connection is not None: 
    3232            return self.connection.rollback() 
    3333 
     34    def _enter_transaction_management(self, managed): 
     35        pass 
     36 
     37    def _leave_transaction_management(self, managed): 
     38        pass 
     39 
    3440    def _savepoint(self, sid): 
    3541        if not self.features.uses_savepoints: 
    3642            return 
     
    7480    # If True, don't use integer foreign keys referring to, e.g., positive 
    7581    # integer primary keys. 
    7682    related_fields_match_type = False 
     83    native_autocommit = False 
     84    can_return_id_from_insert = False 
    7785 
    7886class BaseDatabaseOperations(object): 
    7987    """ 
  • django/db/backends/postgresql_psycopg2/base.py

    old new  
    44Requires psycopg 2: http://initd.org/projects/psycopg2 
    55""" 
    66 
     7from django.conf import settings 
    78from django.db.backends import * 
    89from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations 
    910from django.db.backends.postgresql.client import DatabaseClient 
     
    2829 
    2930class DatabaseFeatures(BaseDatabaseFeatures): 
    3031    needs_datetime_string_cast = False 
    31     uses_savepoints = True 
    3232 
    3333class DatabaseOperations(PostgresqlDatabaseOperations): 
    3434    def last_executed_query(self, cursor, sql, params): 
     
    5959        super(DatabaseWrapper, self).__init__(*args, **kwargs) 
    6060         
    6161        self.features = DatabaseFeatures() 
     62        if settings.DATABASE_OPTIONS.get('native_autocommit', False): 
     63          self.features.native_autocommit = True 
     64          self.features.uses_savepoints = False 
     65          self.features.can_return_id_from_insert = True 
     66          self._isolation_level = 0 
     67        else: 
     68          self.features.native_autocommit = False 
     69          self.features.uses_savepoints = True 
     70          self.features.can_return_id_from_insert = False 
     71          self._isolation_level = 1 
    6272        self.ops = DatabaseOperations() 
    6373        self.client = DatabaseClient() 
    6474        self.creation = DatabaseCreation(self) 
    6575        self.introspection = DatabaseIntrospection(self) 
    6676        self.validation = BaseDatabaseValidation() 
     77  
     78    def _enter_transaction_management(self, managed): 
     79        """Manages the mapping of transaction management to isolation levels""" 
    6780 
     81        if self.features.native_autocommit and managed and self._isolation_level == 0: 
     82            try: 
     83                if self.connection != None: 
     84                    self.connection.set_isolation_level(1) 
     85            finally: 
     86                self._isolation_level = 1 
     87                self.features.uses_savepoints = True 
     88 
     89    def _leave_transaction_management(self, managed): 
     90        """Manages the mapping of transaction management to isolation levels""" 
     91 
     92        if self.features.native_autocommit and not managed and self._isolation_level == 1: 
     93            try: 
     94                if self.connection != None: 
     95                    self.connection.set_isolation_level(0) 
     96            finally: 
     97                self._isolation_level = 0 
     98                self.features.uses_savepoints = False 
     99 
    68100    def _cursor(self, settings): 
    69101        set_tz = False 
    70102        if self.connection is None: 
     
    81113                conn_string += " host=%s" % settings.DATABASE_HOST 
    82114            if settings.DATABASE_PORT: 
    83115                conn_string += " port=%s" % settings.DATABASE_PORT 
    84             self.connection = Database.connect(conn_string, **self.options) 
    85             self.connection.set_isolation_level(1) # make transactions transparent to all cursors 
     116            x = self.options.copy() 
     117            x.pop('native_autocommit', '') 
     118            self.connection = Database.connect(conn_string, **x) 
     119            self.connection.set_isolation_level(self._isolation_level) 
    86120            self.connection.set_client_encoding('UTF8') 
    87121        cursor = self.connection.cursor() 
    88122        cursor.tzinfo_factory = None 
  • django/db/transaction.py

    old new  
    4040# database commit. 
    4141dirty = {} 
    4242 
    43 def enter_transaction_management(): 
     43def enter_transaction_management(managed=True): 
    4444    """ 
    4545    Enters transaction management for a running thread. It must be balanced with 
    4646    the appropriate leave_transaction_management call, since the actual state is 
     
    5858        state[thread_ident].append(settings.TRANSACTIONS_MANAGED) 
    5959    if thread_ident not in dirty: 
    6060        dirty[thread_ident] = False 
     61    connection._enter_transaction_management(managed) 
    6162 
    6263def leave_transaction_management(): 
    6364    """ 
     
    6566    over to the surrounding block, as a commit will commit all changes, even 
    6667    those from outside. (Commits are on connection level.) 
    6768    """ 
     69    connection._leave_transaction_management(is_managed()) 
    6870    thread_ident = thread.get_ident() 
    6971    if thread_ident in state and state[thread_ident]: 
    7072        del state[thread_ident][-1] 
     
    216218    """ 
    217219    def _autocommit(*args, **kw): 
    218220        try: 
    219             enter_transaction_management(
     221            enter_transaction_management(managed=False
    220222            managed(False) 
    221223            return func(*args, **kw) 
    222224        finally: 
  • docs/ref/databases.txt

    old new  
    260260column types have a maximum length restriction of 255 characters, regardless 
    261261of whether ``unique=True`` is specified or not. 
    262262 
     263.. _postgresql-notes: 
     264 
     265PostgreSQL notes 
     266================ 
     267 
     268Django supports PostgreSQL using the psycopg_ package. Django supports both 
     269version 1 and 2. (When you configure Django's database layer, specify either 
     270``postgresql`` [for version 1] or ``postgresql_psycopg2`` [for version 2].) 
     271 
     272Native Autocommit 
     273----------------- 
     274 
     275By default, Django database backends run with a permanently open 
     276transaction, as per the Python DB-API. Django simulates autocommit behaviour 
     277by committing this transaction automatically (e.g. in ``save()``). For 
     278PostgreSQL, this corresponds to running in ``set_isolation_level(1)``. 
     279 
     280For higher performance, ``postgresql_psycopg2`` can be configured to run 
     281without an open connection when Django is in autocommit mode. This 
     282corresponds to PostgreSQL ``set_isolation_level(0)`` and is configured 
     283with:: 
     284 
     285    DATABASE_OPTIONS = {'native_autocommit':True} 
     286 
     287.. _psycopg: http://initd.org/pub/software/psycopg/ 
     288 
    263289.. _sqlite-notes: 
    264290 
    265291SQLite notes