diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 158d936c40d..f24df93bf4f 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -372,47 +372,56 @@ class DatabaseWrapper(BaseDatabaseWrapper): self.connection.ping() return True except DatabaseError: - self.connection.close() - self.connection = None + self.close() return False + def get_connection_params(self): + kwargs = { + 'conv': django_conversions, + 'charset': 'utf8', + 'use_unicode': True, + } + settings_dict = self.settings_dict + if settings_dict['USER']: + kwargs['user'] = settings_dict['USER'] + if settings_dict['NAME']: + kwargs['db'] = settings_dict['NAME'] + if settings_dict['PASSWORD']: + kwargs['passwd'] = force_str(settings_dict['PASSWORD']) + if settings_dict['HOST'].startswith('/'): + kwargs['unix_socket'] = settings_dict['HOST'] + elif settings_dict['HOST']: + kwargs['host'] = settings_dict['HOST'] + if settings_dict['PORT']: + kwargs['port'] = int(settings_dict['PORT']) + # We need the number of potentially affected rows after an + # "UPDATE", not the number of changed rows. + kwargs['client_flag'] = CLIENT.FOUND_ROWS + kwargs.update(settings_dict['OPTIONS']) + return kwargs + + def get_new_connection(self, conn_params): + conn = Database.connect(**conn_params) + conn.encoders[SafeText] = conn.encoders[six.text_type] + conn.encoders[SafeBytes] = conn.encoders[bytes] + return conn + + def init_connection_state(self): + cursor = self.connection.cursor() + # SQL_AUTO_IS_NULL in MySQL controls whether an AUTO_INCREMENT column + # on a recently-inserted row will return when the field is tested for + # NULL. Disabling this value brings this aspect of MySQL in line with + # SQL standards. + cursor.execute('SET SQL_AUTO_IS_NULL = 0') + cursor.close() + def _cursor(self): - new_connection = False if not self._valid_connection(): - new_connection = True - kwargs = { - 'conv': django_conversions, - 'charset': 'utf8', - 'use_unicode': True, - } - settings_dict = self.settings_dict - if settings_dict['USER']: - kwargs['user'] = settings_dict['USER'] - if settings_dict['NAME']: - kwargs['db'] = settings_dict['NAME'] - if settings_dict['PASSWORD']: - kwargs['passwd'] = force_str(settings_dict['PASSWORD']) - if settings_dict['HOST'].startswith('/'): - kwargs['unix_socket'] = settings_dict['HOST'] - elif settings_dict['HOST']: - kwargs['host'] = settings_dict['HOST'] - if settings_dict['PORT']: - kwargs['port'] = int(settings_dict['PORT']) - # We need the number of potentially affected rows after an - # "UPDATE", not the number of changed rows. - kwargs['client_flag'] = CLIENT.FOUND_ROWS - kwargs.update(settings_dict['OPTIONS']) - self.connection = Database.connect(**kwargs) - self.connection.encoders[SafeText] = self.connection.encoders[six.text_type] - self.connection.encoders[SafeBytes] = self.connection.encoders[bytes] + conn_params = self.get_connection_params() + self.connection = self.get_new_connection(conn_params) + self.init_connection_state() connection_created.send(sender=self.__class__, connection=self) cursor = self.connection.cursor() - if new_connection: - # SQL_AUTO_IS_NULL in MySQL controls whether an AUTO_INCREMENT column - # on a recently-inserted row will return when the field is tested for - # NULL. Disabling this value brings this aspect of MySQL in line with - # SQL standards. - cursor.execute('SET SQL_AUTO_IS_NULL = 0') return CursorWrapper(cursor) def _rollback(self): @@ -433,8 +442,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): server_info = self.connection.get_server_info() if new_connection: # Make sure we close the connection - self.connection.close() - self.connection = None + self.close() m = server_version_re.match(server_info) if not m: raise Exception('Unable to determine MySQL version from version string %r' % server_info) diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index dfdfd4fd498..c6f072df3b7 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -489,66 +489,78 @@ class DatabaseWrapper(BaseDatabaseWrapper): return "%s/%s@%s" % (settings_dict['USER'], settings_dict['PASSWORD'], dsn) + def create_cursor(self, conn): + return FormatStylePlaceholderCursor(conn) + + def get_connection_params(self): + conn_params = self.settings_dict['OPTIONS'].copy() + if 'use_returning_into' in conn_params: + del conn_params['use_returning_into'] + return conn_params + + def get_new_connection(self, conn_params): + conn_string = convert_unicode(self._connect_string()) + return Database.connect(conn_string, **conn_params) + + def init_connection_state(self): + cursor = self.create_cursor(self.connection) + # Set the territory first. The territory overrides NLS_DATE_FORMAT + # and NLS_TIMESTAMP_FORMAT to the territory default. When all of + # these are set in single statement it isn't clear what is supposed + # to happen. + cursor.execute("ALTER SESSION SET NLS_TERRITORY = 'AMERICA'") + # Set oracle date to ansi date format. This only needs to execute + # once when we create a new connection. We also set the Territory + # to 'AMERICA' which forces Sunday to evaluate to a '1' in + # TO_CHAR(). + cursor.execute( + "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" + " NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'" + + (" TIME_ZONE = 'UTC'" if settings.USE_TZ else '')) + cursor.close() + if 'operators' not in self.__dict__: + # Ticket #14149: Check whether our LIKE implementation will + # work for this connection or we need to fall back on LIKEC. + # This check is performed only once per DatabaseWrapper + # instance per thread, since subsequent connections will use + # the same settings. + cursor = self.create_cursor(self.connection) + try: + cursor.execute("SELECT 1 FROM DUAL WHERE DUMMY %s" + % self._standard_operators['contains'], + ['X']) + except utils.DatabaseError: + self.operators = self._likec_operators + else: + self.operators = self._standard_operators + cursor.close() + + try: + self.oracle_version = int(self.connection.version.split('.')[0]) + # There's no way for the DatabaseOperations class to know the + # currently active Oracle version, so we do some setups here. + # TODO: Multi-db support will need a better solution (a way to + # communicate the current version). + if self.oracle_version <= 9: + self.ops.regex_lookup = self.ops.regex_lookup_9 + else: + self.ops.regex_lookup = self.ops.regex_lookup_10 + except ValueError: + pass + try: + self.connection.stmtcachesize = 20 + except: + # Django docs specify cx_Oracle version 4.3.1 or higher, but + # stmtcachesize is available only in 4.3.2 and up. + pass + def _cursor(self): - cursor = None if not self._valid_connection(): - conn_string = convert_unicode(self._connect_string()) - conn_params = self.settings_dict['OPTIONS'].copy() - if 'use_returning_into' in conn_params: - del conn_params['use_returning_into'] - self.connection = Database.connect(conn_string, **conn_params) - cursor = FormatStylePlaceholderCursor(self.connection) - # Set the territory first. The territory overrides NLS_DATE_FORMAT - # and NLS_TIMESTAMP_FORMAT to the territory default. When all of - # these are set in single statement it isn't clear what is supposed - # to happen. - cursor.execute("ALTER SESSION SET NLS_TERRITORY = 'AMERICA'") - # Set oracle date to ansi date format. This only needs to execute - # once when we create a new connection. We also set the Territory - # to 'AMERICA' which forces Sunday to evaluate to a '1' in - # TO_CHAR(). - cursor.execute( - "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" - " NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'" - + (" TIME_ZONE = 'UTC'" if settings.USE_TZ else '')) - - if 'operators' not in self.__dict__: - # Ticket #14149: Check whether our LIKE implementation will - # work for this connection or we need to fall back on LIKEC. - # This check is performed only once per DatabaseWrapper - # instance per thread, since subsequent connections will use - # the same settings. - try: - cursor.execute("SELECT 1 FROM DUAL WHERE DUMMY %s" - % self._standard_operators['contains'], - ['X']) - except utils.DatabaseError: - self.operators = self._likec_operators - else: - self.operators = self._standard_operators - - try: - self.oracle_version = int(self.connection.version.split('.')[0]) - # There's no way for the DatabaseOperations class to know the - # currently active Oracle version, so we do some setups here. - # TODO: Multi-db support will need a better solution (a way to - # communicate the current version). - if self.oracle_version <= 9: - self.ops.regex_lookup = self.ops.regex_lookup_9 - else: - self.ops.regex_lookup = self.ops.regex_lookup_10 - except ValueError: - pass - try: - self.connection.stmtcachesize = 20 - except: - # Django docs specify cx_Oracle version 4.3.1 or higher, but - # stmtcachesize is available only in 4.3.2 and up. - pass + conn_params = self.get_connection_params() + self.connection = self.get_new_connection(conn_params) + self.init_connection_state() connection_created.send(sender=self.__class__, connection=self) - if not cursor: - cursor = FormatStylePlaceholderCursor(self.connection) - return cursor + return self.create_cursor(self.connection) # Oracle doesn't support savepoint commits. Ignore them. def _savepoint_commit(self, sid): diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 5837922061b..013aaaa91ed 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -156,48 +156,59 @@ class DatabaseWrapper(BaseDatabaseWrapper): return self._pg_version pg_version = property(_get_pg_version) - def _cursor(self): + def get_connection_params(self): settings_dict = self.settings_dict - if self.connection is None: - if not settings_dict['NAME']: - from django.core.exceptions import ImproperlyConfigured - raise ImproperlyConfigured( - "settings.DATABASES is improperly configured. " - "Please supply the NAME value.") - conn_params = { - 'database': settings_dict['NAME'], - } - conn_params.update(settings_dict['OPTIONS']) - if 'autocommit' in conn_params: - del conn_params['autocommit'] - if settings_dict['USER']: - conn_params['user'] = settings_dict['USER'] - if settings_dict['PASSWORD']: - conn_params['password'] = force_str(settings_dict['PASSWORD']) - if settings_dict['HOST']: - conn_params['host'] = settings_dict['HOST'] - if settings_dict['PORT']: - conn_params['port'] = settings_dict['PORT'] - self.connection = Database.connect(**conn_params) - self.connection.set_client_encoding('UTF8') - tz = 'UTC' if settings.USE_TZ else settings_dict.get('TIME_ZONE') - if tz: - try: - get_parameter_status = self.connection.get_parameter_status - except AttributeError: - # psycopg2 < 2.0.12 doesn't have get_parameter_status - conn_tz = None - else: - conn_tz = get_parameter_status('TimeZone') + if not settings_dict['NAME']: + from django.core.exceptions import ImproperlyConfigured + raise ImproperlyConfigured( + "settings.DATABASES is improperly configured. " + "Please supply the NAME value.") + conn_params = { + 'database': settings_dict['NAME'], + } + conn_params.update(settings_dict['OPTIONS']) + if 'autocommit' in conn_params: + del conn_params['autocommit'] + if settings_dict['USER']: + conn_params['user'] = settings_dict['USER'] + if settings_dict['PASSWORD']: + conn_params['password'] = force_str(settings_dict['PASSWORD']) + if settings_dict['HOST']: + conn_params['host'] = settings_dict['HOST'] + if settings_dict['PORT']: + conn_params['port'] = settings_dict['PORT'] + return conn_params - if conn_tz != tz: - # Set the time zone in autocommit mode (see #17062) - self.connection.set_isolation_level( - psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) - self.connection.cursor().execute( - self.ops.set_time_zone_sql(), [tz]) - self.connection.set_isolation_level(self.isolation_level) - self._get_pg_version() + def get_new_connection(self, conn_params): + return Database.connect(**conn_params) + + def init_connection_state(self): + settings_dict = self.settings_dict + self.connection.set_client_encoding('UTF8') + tz = 'UTC' if settings.USE_TZ else settings_dict.get('TIME_ZONE') + if tz: + try: + get_parameter_status = self.connection.get_parameter_status + except AttributeError: + # psycopg2 < 2.0.12 doesn't have get_parameter_status + conn_tz = None + else: + conn_tz = get_parameter_status('TimeZone') + + if conn_tz != tz: + # Set the time zone in autocommit mode (see #17062) + self.connection.set_isolation_level( + psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + self.connection.cursor().execute( + self.ops.set_time_zone_sql(), [tz]) + self.connection.set_isolation_level(self.isolation_level) + self._get_pg_version() + + def _cursor(self): + if self.connection is None: + conn_params = self.get_connection_params() + self.connection = self.get_new_connection(conn_params) + self.init_connection_state() connection_created.send(sender=self.__class__, connection=self) cursor = self.connection.cursor() cursor.tzinfo_factory = utc_tzinfo_factory if settings.USE_TZ else None diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 6b6c6b67d91..1fcc222c80b 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -266,7 +266,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): self.introspection = DatabaseIntrospection(self) self.validation = BaseDatabaseValidation(self) - def _sqlite_create_connection(self): + def get_connection_params(self): settings_dict = self.settings_dict if not settings_dict['NAME']: from django.core.exceptions import ImproperlyConfigured @@ -293,12 +293,24 @@ class DatabaseWrapper(BaseDatabaseWrapper): RuntimeWarning ) kwargs.update({'check_same_thread': False}) - self.connection = Database.connect(**kwargs) + return kwargs + + def get_new_connection(self, conn_params): + conn = Database.connect(**conn_params) # Register extract, date_trunc, and regexp functions. - self.connection.create_function("django_extract", 2, _sqlite_extract) - self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc) - self.connection.create_function("regexp", 2, _sqlite_regexp) - self.connection.create_function("django_format_dtdelta", 5, _sqlite_format_dtdelta) + conn.create_function("django_extract", 2, _sqlite_extract) + conn.create_function("django_date_trunc", 2, _sqlite_date_trunc) + conn.create_function("regexp", 2, _sqlite_regexp) + conn.create_function("django_format_dtdelta", 5, _sqlite_format_dtdelta) + return conn + + def init_connection_state(self): + pass + + def _sqlite_create_connection(self): + conn_params = self.get_connection_params() + self.connection = self.get_new_connection(conn_params) + self.init_connection_state() connection_created.send(sender=self.__class__, connection=self) def _cursor(self):