|  | @@ -0,0 +1,376 @@
 | 
											
												
													
														|  | 
 |  | +"""
 | 
											
												
													
														|  | 
 |  | +MySQL database backend for Django.
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +Requires mysqlclient: https://pypi.python.org/pypi/mysqlclient/
 | 
											
												
													
														|  | 
 |  | +MySQLdb is supported for Python 2 only: http://sourceforge.net/projects/mysql-python
 | 
											
												
													
														|  | 
 |  | +"""
 | 
											
												
													
														|  | 
 |  | +from __future__ import unicode_literals
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +import datetime
 | 
											
												
													
														|  | 
 |  | +import re
 | 
											
												
													
														|  | 
 |  | +import sys
 | 
											
												
													
														|  | 
 |  | +import warnings
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +from django.conf import settings
 | 
											
												
													
														|  | 
 |  | +from django.db import utils
 | 
											
												
													
														|  | 
 |  | +from django.db.backends import utils as backend_utils
 | 
											
												
													
														|  | 
 |  | +from django.db.backends.base.base import BaseDatabaseWrapper
 | 
											
												
													
														|  | 
 |  | +from django.utils import six, timezone
 | 
											
												
													
														|  | 
 |  | +from django.utils.encoding import force_str
 | 
											
												
													
														|  | 
 |  | +from django.utils.functional import cached_property
 | 
											
												
													
														|  | 
 |  | +from django.utils.safestring import SafeBytes, SafeText
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +try:
 | 
											
												
													
														|  | 
 |  | +    import MySQLdb as Database
 | 
											
												
													
														|  | 
 |  | +except ImportError as e:
 | 
											
												
													
														|  | 
 |  | +    from django.core.exceptions import ImproperlyConfigured
 | 
											
												
													
														|  | 
 |  | +    raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +from MySQLdb.constants import CLIENT, FIELD_TYPE                # isort:skip
 | 
											
												
													
														|  | 
 |  | +from MySQLdb.converters import Thing2Literal, conversions       # isort:skip
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +# Some of these import MySQLdb, so import them after checking if it's installed.
 | 
											
												
													
														|  | 
 |  | +from .client import DatabaseClient                          # isort:skip
 | 
											
												
													
														|  | 
 |  | +from .creation import DatabaseCreation                      # isort:skip
 | 
											
												
													
														|  | 
 |  | +from .features import DatabaseFeatures                      # isort:skip
 | 
											
												
													
														|  | 
 |  | +from .introspection import DatabaseIntrospection            # isort:skip
 | 
											
												
													
														|  | 
 |  | +from .operations import DatabaseOperations                  # isort:skip
 | 
											
												
													
														|  | 
 |  | +from .schema import DatabaseSchemaEditor                    # isort:skip
 | 
											
												
													
														|  | 
 |  | +from .validation import DatabaseValidation                  # isort:skip
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +# We want version (1, 2, 1, 'final', 2) or later. We can't just use
 | 
											
												
													
														|  | 
 |  | +# lexicographic ordering in this check because then (1, 2, 1, 'gamma')
 | 
											
												
													
														|  | 
 |  | +# inadvertently passes the version test.
 | 
											
												
													
														|  | 
 |  | +version = Database.version_info
 | 
											
												
													
														|  | 
 |  | +if (version < (1, 2, 1) or (version[:3] == (1, 2, 1) and
 | 
											
												
													
														|  | 
 |  | +        (len(version) < 5 or version[3] != 'final' or version[4] < 2))):
 | 
											
												
													
														|  | 
 |  | +    from django.core.exceptions import ImproperlyConfigured
 | 
											
												
													
														|  | 
 |  | +    raise ImproperlyConfigured("MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +DatabaseError = Database.DatabaseError
 | 
											
												
													
														|  | 
 |  | +IntegrityError = Database.IntegrityError
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +# It's impossible to import datetime_or_None directly from MySQLdb.times
 | 
											
												
													
														|  | 
 |  | +parse_datetime = conversions[FIELD_TYPE.DATETIME]
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +def parse_datetime_with_timezone_support(value):
 | 
											
												
													
														|  | 
 |  | +    dt = parse_datetime(value)
 | 
											
												
													
														|  | 
 |  | +    # Confirm that dt is naive before overwriting its tzinfo.
 | 
											
												
													
														|  | 
 |  | +    if dt is not None and settings.USE_TZ and timezone.is_naive(dt):
 | 
											
												
													
														|  | 
 |  | +        dt = dt.replace(tzinfo=timezone.utc)
 | 
											
												
													
														|  | 
 |  | +    return dt
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +def adapt_datetime_with_timezone_support(value, conv):
 | 
											
												
													
														|  | 
 |  | +    # Equivalent to DateTimeField.get_db_prep_value. Used only by raw SQL.
 | 
											
												
													
														|  | 
 |  | +    if settings.USE_TZ:
 | 
											
												
													
														|  | 
 |  | +        if timezone.is_naive(value):
 | 
											
												
													
														|  | 
 |  | +            warnings.warn("MySQL received a naive datetime (%s)"
 | 
											
												
													
														|  | 
 |  | +                          " while time zone support is active." % value,
 | 
											
												
													
														|  | 
 |  | +                          RuntimeWarning)
 | 
											
												
													
														|  | 
 |  | +            default_timezone = timezone.get_default_timezone()
 | 
											
												
													
														|  | 
 |  | +            value = timezone.make_aware(value, default_timezone)
 | 
											
												
													
														|  | 
 |  | +        value = value.astimezone(timezone.utc).replace(tzinfo=None)
 | 
											
												
													
														|  | 
 |  | +    return Thing2Literal(value.strftime("%Y-%m-%d %H:%M:%S.%f"), conv)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +# MySQLdb-1.2.1 returns TIME columns as timedelta -- they are more like
 | 
											
												
													
														|  | 
 |  | +# timedelta in terms of actual behavior as they are signed and include days --
 | 
											
												
													
														|  | 
 |  | +# and Django expects time, so we still need to override that. We also need to
 | 
											
												
													
														|  | 
 |  | +# add special handling for SafeText and SafeBytes as MySQLdb's type
 | 
											
												
													
														|  | 
 |  | +# checking is too tight to catch those (see Django ticket #6052).
 | 
											
												
													
														|  | 
 |  | +# Finally, MySQLdb always returns naive datetime objects. However, when
 | 
											
												
													
														|  | 
 |  | +# timezone support is active, Django expects timezone-aware datetime objects.
 | 
											
												
													
														|  | 
 |  | +django_conversions = conversions.copy()
 | 
											
												
													
														|  | 
 |  | +django_conversions.update({
 | 
											
												
													
														|  | 
 |  | +    FIELD_TYPE.TIME: backend_utils.typecast_time,
 | 
											
												
													
														|  | 
 |  | +    FIELD_TYPE.DECIMAL: backend_utils.typecast_decimal,
 | 
											
												
													
														|  | 
 |  | +    FIELD_TYPE.NEWDECIMAL: backend_utils.typecast_decimal,
 | 
											
												
													
														|  | 
 |  | +    FIELD_TYPE.DATETIME: parse_datetime_with_timezone_support,
 | 
											
												
													
														|  | 
 |  | +    datetime.datetime: adapt_datetime_with_timezone_support,
 | 
											
												
													
														|  | 
 |  | +})
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +# This should match the numerical portion of the version numbers (we can treat
 | 
											
												
													
														|  | 
 |  | +# versions like 5.0.24 and 5.0.24a as the same). Based on the list of version
 | 
											
												
													
														|  | 
 |  | +# at http://dev.mysql.com/doc/refman/4.1/en/news.html and
 | 
											
												
													
														|  | 
 |  | +# http://dev.mysql.com/doc/refman/5.0/en/news.html .
 | 
											
												
													
														|  | 
 |  | +server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})')
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +# MySQLdb-1.2.1 and newer automatically makes use of SHOW WARNINGS on
 | 
											
												
													
														|  | 
 |  | +# MySQL-4.1 and newer, so the MysqlDebugWrapper is unnecessary. Since the
 | 
											
												
													
														|  | 
 |  | +# point is to raise Warnings as exceptions, this can be done with the Python
 | 
											
												
													
														|  | 
 |  | +# warning module, and this is setup when the connection is created, and the
 | 
											
												
													
														|  | 
 |  | +# standard backend_utils.CursorDebugWrapper can be used. Also, using sql_mode
 | 
											
												
													
														|  | 
 |  | +# TRADITIONAL will automatically cause most warnings to be treated as errors.
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +class CursorWrapper(object):
 | 
											
												
													
														|  | 
 |  | +    """
 | 
											
												
													
														|  | 
 |  | +    A thin wrapper around MySQLdb's normal cursor class so that we can catch
 | 
											
												
													
														|  | 
 |  | +    particular exception instances and reraise them with the right types.
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    Implemented as a wrapper, rather than a subclass, so that we aren't stuck
 | 
											
												
													
														|  | 
 |  | +    to the particular underlying representation returned by Connection.cursor().
 | 
											
												
													
														|  | 
 |  | +    """
 | 
											
												
													
														|  | 
 |  | +    codes_for_integrityerror = (1048,)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def __init__(self, cursor):
 | 
											
												
													
														|  | 
 |  | +        self.cursor = cursor
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def execute(self, query, args=None):
 | 
											
												
													
														|  | 
 |  | +        try:
 | 
											
												
													
														|  | 
 |  | +            # args is None means no string interpolation
 | 
											
												
													
														|  | 
 |  | +            return self.cursor.execute(query, args)
 | 
											
												
													
														|  | 
 |  | +        except Database.OperationalError as e:
 | 
											
												
													
														|  | 
 |  | +            # Map some error codes to IntegrityError, since they seem to be
 | 
											
												
													
														|  | 
 |  | +            # misclassified and Django would prefer the more logical place.
 | 
											
												
													
														|  | 
 |  | +            if e.args[0] in self.codes_for_integrityerror:
 | 
											
												
													
														|  | 
 |  | +                six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
 | 
											
												
													
														|  | 
 |  | +            raise
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def executemany(self, query, args):
 | 
											
												
													
														|  | 
 |  | +        try:
 | 
											
												
													
														|  | 
 |  | +            return self.cursor.executemany(query, args)
 | 
											
												
													
														|  | 
 |  | +        except Database.OperationalError as e:
 | 
											
												
													
														|  | 
 |  | +            # Map some error codes to IntegrityError, since they seem to be
 | 
											
												
													
														|  | 
 |  | +            # misclassified and Django would prefer the more logical place.
 | 
											
												
													
														|  | 
 |  | +            if e.args[0] in self.codes_for_integrityerror:
 | 
											
												
													
														|  | 
 |  | +                six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
 | 
											
												
													
														|  | 
 |  | +            raise
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def __getattr__(self, attr):
 | 
											
												
													
														|  | 
 |  | +        if attr in self.__dict__:
 | 
											
												
													
														|  | 
 |  | +            return self.__dict__[attr]
 | 
											
												
													
														|  | 
 |  | +        else:
 | 
											
												
													
														|  | 
 |  | +            return getattr(self.cursor, attr)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def __iter__(self):
 | 
											
												
													
														|  | 
 |  | +        return iter(self.cursor)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def __enter__(self):
 | 
											
												
													
														|  | 
 |  | +        return self
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def __exit__(self, type, value, traceback):
 | 
											
												
													
														|  | 
 |  | +        # Ticket #17671 - Close instead of passing thru to avoid backend
 | 
											
												
													
														|  | 
 |  | +        # specific behavior.
 | 
											
												
													
														|  | 
 |  | +        self.close()
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +class DatabaseWrapper(BaseDatabaseWrapper):
 | 
											
												
													
														|  | 
 |  | +    vendor = 'mysql'
 | 
											
												
													
														|  | 
 |  | +    # This dictionary maps Field objects to their associated MySQL column
 | 
											
												
													
														|  | 
 |  | +    # types, as strings. Column-type strings can contain format strings; they'll
 | 
											
												
													
														|  | 
 |  | +    # be interpolated against the values of Field.__dict__ before being output.
 | 
											
												
													
														|  | 
 |  | +    # If a column type is set to None, it won't be included in the output.
 | 
											
												
													
														|  | 
 |  | +    _data_types = {
 | 
											
												
													
														|  | 
 |  | +        'AutoField': 'integer AUTO_INCREMENT',
 | 
											
												
													
														|  | 
 |  | +        'BinaryField': 'longblob',
 | 
											
												
													
														|  | 
 |  | +        'BooleanField': 'bool',
 | 
											
												
													
														|  | 
 |  | +        'CharField': 'varchar(%(max_length)s)',
 | 
											
												
													
														|  | 
 |  | +        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
 | 
											
												
													
														|  | 
 |  | +        'DateField': 'date',
 | 
											
												
													
														|  | 
 |  | +        'DateTimeField': 'datetime',
 | 
											
												
													
														|  | 
 |  | +        'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
 | 
											
												
													
														|  | 
 |  | +        'DurationField': 'bigint',
 | 
											
												
													
														|  | 
 |  | +        'FileField': 'varchar(%(max_length)s)',
 | 
											
												
													
														|  | 
 |  | +        'FilePathField': 'varchar(%(max_length)s)',
 | 
											
												
													
														|  | 
 |  | +        'FloatField': 'double precision',
 | 
											
												
													
														|  | 
 |  | +        'IntegerField': 'integer',
 | 
											
												
													
														|  | 
 |  | +        'BigIntegerField': 'bigint',
 | 
											
												
													
														|  | 
 |  | +        'IPAddressField': 'char(15)',
 | 
											
												
													
														|  | 
 |  | +        'GenericIPAddressField': 'char(39)',
 | 
											
												
													
														|  | 
 |  | +        'NullBooleanField': 'bool',
 | 
											
												
													
														|  | 
 |  | +        'OneToOneField': 'integer',
 | 
											
												
													
														|  | 
 |  | +        'PositiveIntegerField': 'integer UNSIGNED',
 | 
											
												
													
														|  | 
 |  | +        'PositiveSmallIntegerField': 'smallint UNSIGNED',
 | 
											
												
													
														|  | 
 |  | +        'SlugField': 'varchar(%(max_length)s)',
 | 
											
												
													
														|  | 
 |  | +        'SmallIntegerField': 'smallint',
 | 
											
												
													
														|  | 
 |  | +        'TextField': 'longtext',
 | 
											
												
													
														|  | 
 |  | +        'TimeField': 'time',
 | 
											
												
													
														|  | 
 |  | +        'UUIDField': 'char(32)',
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    @cached_property
 | 
											
												
													
														|  | 
 |  | +    def data_types(self):
 | 
											
												
													
														|  | 
 |  | +        if self.features.supports_microsecond_precision:
 | 
											
												
													
														|  | 
 |  | +            return dict(self._data_types, DateTimeField='datetime(6)', TimeField='time(6)')
 | 
											
												
													
														|  | 
 |  | +        else:
 | 
											
												
													
														|  | 
 |  | +            return self._data_types
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    operators = {
 | 
											
												
													
														|  | 
 |  | +        'exact': '= %s',
 | 
											
												
													
														|  | 
 |  | +        'iexact': 'LIKE %s',
 | 
											
												
													
														|  | 
 |  | +        'contains': 'LIKE BINARY %s',
 | 
											
												
													
														|  | 
 |  | +        'icontains': 'LIKE %s',
 | 
											
												
													
														|  | 
 |  | +        'regex': 'REGEXP BINARY %s',
 | 
											
												
													
														|  | 
 |  | +        'iregex': 'REGEXP %s',
 | 
											
												
													
														|  | 
 |  | +        'gt': '> %s',
 | 
											
												
													
														|  | 
 |  | +        'gte': '>= %s',
 | 
											
												
													
														|  | 
 |  | +        'lt': '< %s',
 | 
											
												
													
														|  | 
 |  | +        'lte': '<= %s',
 | 
											
												
													
														|  | 
 |  | +        'startswith': 'LIKE BINARY %s',
 | 
											
												
													
														|  | 
 |  | +        'endswith': 'LIKE BINARY %s',
 | 
											
												
													
														|  | 
 |  | +        'istartswith': 'LIKE %s',
 | 
											
												
													
														|  | 
 |  | +        'iendswith': 'LIKE %s',
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    # The patterns below are used to generate SQL pattern lookup clauses when
 | 
											
												
													
														|  | 
 |  | +    # the right-hand side of the lookup isn't a raw string (it might be an expression
 | 
											
												
													
														|  | 
 |  | +    # or the result of a bilateral transformation).
 | 
											
												
													
														|  | 
 |  | +    # In those cases, special characters for LIKE operators (e.g. \, *, _) should be
 | 
											
												
													
														|  | 
 |  | +    # escaped on database side.
 | 
											
												
													
														|  | 
 |  | +    #
 | 
											
												
													
														|  | 
 |  | +    # Note: we use str.format() here for readability as '%' is used as a wildcard for
 | 
											
												
													
														|  | 
 |  | +    # the LIKE operator.
 | 
											
												
													
														|  | 
 |  | +    pattern_esc = r"REPLACE(REPLACE(REPLACE({}, '\\', '\\\\'), '%%', '\%%'), '_', '\_')"
 | 
											
												
													
														|  | 
 |  | +    pattern_ops = {
 | 
											
												
													
														|  | 
 |  | +        'contains': "LIKE BINARY CONCAT('%%', {}, '%%')",
 | 
											
												
													
														|  | 
 |  | +        'icontains': "LIKE CONCAT('%%', {}, '%%')",
 | 
											
												
													
														|  | 
 |  | +        'startswith': "LIKE BINARY CONCAT({}, '%%')",
 | 
											
												
													
														|  | 
 |  | +        'istartswith': "LIKE CONCAT({}, '%%')",
 | 
											
												
													
														|  | 
 |  | +        'endswith': "LIKE BINARY CONCAT('%%', {})",
 | 
											
												
													
														|  | 
 |  | +        'iendswith': "LIKE CONCAT('%%', {})",
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    Database = Database
 | 
											
												
													
														|  | 
 |  | +    SchemaEditorClass = DatabaseSchemaEditor
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def __init__(self, *args, **kwargs):
 | 
											
												
													
														|  | 
 |  | +        super(DatabaseWrapper, self).__init__(*args, **kwargs)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        self.features = DatabaseFeatures(self)
 | 
											
												
													
														|  | 
 |  | +        self.ops = DatabaseOperations(self)
 | 
											
												
													
														|  | 
 |  | +        self.client = DatabaseClient(self)
 | 
											
												
													
														|  | 
 |  | +        self.creation = DatabaseCreation(self)
 | 
											
												
													
														|  | 
 |  | +        self.introspection = DatabaseIntrospection(self)
 | 
											
												
													
														|  | 
 |  | +        self.validation = DatabaseValidation(self)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def get_connection_params(self):
 | 
											
												
													
														|  | 
 |  | +        kwargs = {
 | 
											
												
													
														|  | 
 |  | +            'conv': django_conversions,
 | 
											
												
													
														|  | 
 |  | +            'charset': 'utf8',
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +        if six.PY2:
 | 
											
												
													
														|  | 
 |  | +            kwargs['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):
 | 
											
												
													
														|  | 
 |  | +        with self.cursor() as 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')
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def create_cursor(self):
 | 
											
												
													
														|  | 
 |  | +        cursor = self.connection.cursor()
 | 
											
												
													
														|  | 
 |  | +        return CursorWrapper(cursor)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def _rollback(self):
 | 
											
												
													
														|  | 
 |  | +        try:
 | 
											
												
													
														|  | 
 |  | +            BaseDatabaseWrapper._rollback(self)
 | 
											
												
													
														|  | 
 |  | +        except Database.NotSupportedError:
 | 
											
												
													
														|  | 
 |  | +            pass
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def _set_autocommit(self, autocommit):
 | 
											
												
													
														|  | 
 |  | +        with self.wrap_database_errors:
 | 
											
												
													
														|  | 
 |  | +            self.connection.autocommit(autocommit)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def disable_constraint_checking(self):
 | 
											
												
													
														|  | 
 |  | +        """
 | 
											
												
													
														|  | 
 |  | +        Disables foreign key checks, primarily for use in adding rows with forward references. Always returns True,
 | 
											
												
													
														|  | 
 |  | +        to indicate constraint checks need to be re-enabled.
 | 
											
												
													
														|  | 
 |  | +        """
 | 
											
												
													
														|  | 
 |  | +        self.cursor().execute('SET foreign_key_checks=0')
 | 
											
												
													
														|  | 
 |  | +        return True
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def enable_constraint_checking(self):
 | 
											
												
													
														|  | 
 |  | +        """
 | 
											
												
													
														|  | 
 |  | +        Re-enable foreign key checks after they have been disabled.
 | 
											
												
													
														|  | 
 |  | +        """
 | 
											
												
													
														|  | 
 |  | +        # Override needs_rollback in case constraint_checks_disabled is
 | 
											
												
													
														|  | 
 |  | +        # nested inside transaction.atomic.
 | 
											
												
													
														|  | 
 |  | +        self.needs_rollback, needs_rollback = False, self.needs_rollback
 | 
											
												
													
														|  | 
 |  | +        try:
 | 
											
												
													
														|  | 
 |  | +            self.cursor().execute('SET foreign_key_checks=1')
 | 
											
												
													
														|  | 
 |  | +        finally:
 | 
											
												
													
														|  | 
 |  | +            self.needs_rollback = needs_rollback
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def check_constraints(self, table_names=None):
 | 
											
												
													
														|  | 
 |  | +        """
 | 
											
												
													
														|  | 
 |  | +        Checks each table name in `table_names` for rows with invalid foreign
 | 
											
												
													
														|  | 
 |  | +        key references. This method is intended to be used in conjunction with
 | 
											
												
													
														|  | 
 |  | +        `disable_constraint_checking()` and `enable_constraint_checking()`, to
 | 
											
												
													
														|  | 
 |  | +        determine if rows with invalid references were entered while constraint
 | 
											
												
													
														|  | 
 |  | +        checks were off.
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        Raises an IntegrityError on the first invalid foreign key reference
 | 
											
												
													
														|  | 
 |  | +        encountered (if any) and provides detailed information about the
 | 
											
												
													
														|  | 
 |  | +        invalid reference in the error message.
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        Backends can override this method if they can more directly apply
 | 
											
												
													
														|  | 
 |  | +        constraint checking (e.g. via "SET CONSTRAINTS ALL IMMEDIATE")
 | 
											
												
													
														|  | 
 |  | +        """
 | 
											
												
													
														|  | 
 |  | +        cursor = self.cursor()
 | 
											
												
													
														|  | 
 |  | +        if table_names is None:
 | 
											
												
													
														|  | 
 |  | +            table_names = self.introspection.table_names(cursor)
 | 
											
												
													
														|  | 
 |  | +        for table_name in table_names:
 | 
											
												
													
														|  | 
 |  | +            primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
 | 
											
												
													
														|  | 
 |  | +            if not primary_key_column_name:
 | 
											
												
													
														|  | 
 |  | +                continue
 | 
											
												
													
														|  | 
 |  | +            key_columns = self.introspection.get_key_columns(cursor, table_name)
 | 
											
												
													
														|  | 
 |  | +            for column_name, referenced_table_name, referenced_column_name in key_columns:
 | 
											
												
													
														|  | 
 |  | +                cursor.execute("""
 | 
											
												
													
														|  | 
 |  | +                    SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING
 | 
											
												
													
														|  | 
 |  | +                    LEFT JOIN `%s` as REFERRED
 | 
											
												
													
														|  | 
 |  | +                    ON (REFERRING.`%s` = REFERRED.`%s`)
 | 
											
												
													
														|  | 
 |  | +                    WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL"""
 | 
											
												
													
														|  | 
 |  | +                    % (primary_key_column_name, column_name, table_name, referenced_table_name,
 | 
											
												
													
														|  | 
 |  | +                    column_name, referenced_column_name, column_name, referenced_column_name))
 | 
											
												
													
														|  | 
 |  | +                for bad_row in cursor.fetchall():
 | 
											
												
													
														|  | 
 |  | +                    raise utils.IntegrityError("The row in table '%s' with primary key '%s' has an invalid "
 | 
											
												
													
														|  | 
 |  | +                        "foreign key: %s.%s contains a value '%s' that does not have a corresponding value in %s.%s."
 | 
											
												
													
														|  | 
 |  | +                        % (table_name, bad_row[0],
 | 
											
												
													
														|  | 
 |  | +                        table_name, column_name, bad_row[1],
 | 
											
												
													
														|  | 
 |  | +                        referenced_table_name, referenced_column_name))
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def is_usable(self):
 | 
											
												
													
														|  | 
 |  | +        try:
 | 
											
												
													
														|  | 
 |  | +            self.connection.ping()
 | 
											
												
													
														|  | 
 |  | +        except Database.Error:
 | 
											
												
													
														|  | 
 |  | +            return False
 | 
											
												
													
														|  | 
 |  | +        else:
 | 
											
												
													
														|  | 
 |  | +            return True
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    @cached_property
 | 
											
												
													
														|  | 
 |  | +    def mysql_version(self):
 | 
											
												
													
														|  | 
 |  | +        with self.temporary_connection():
 | 
											
												
													
														|  | 
 |  | +            server_info = self.connection.get_server_info()
 | 
											
												
													
														|  | 
 |  | +        match = server_version_re.match(server_info)
 | 
											
												
													
														|  | 
 |  | +        if not match:
 | 
											
												
													
														|  | 
 |  | +            raise Exception('Unable to determine MySQL version from version string %r' % server_info)
 | 
											
												
													
														|  | 
 |  | +        return tuple(int(x) for x in match.groups())
 |