class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'mysql' 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', } Database = mysql.connector def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.server_version = None # Since some features depend on the MySQL version, we need to connect self._connect() 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 _valid_connection(self): if self.connection: return self.connection.is_connected() return False def get_connection_params(self): # Django 1.6 kwargs = { 'charset': 'utf8', 'use_unicode': True, 'buffered': True, } settings_dict = self.settings_dict if settings_dict['USER']: kwargs['user'] = settings_dict['USER'] if settings_dict['NAME']: kwargs['database'] = settings_dict['NAME'] if settings_dict['PASSWORD']: kwargs['passwd'] = 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']) # Raise exceptions for database warnings if DEBUG is on kwargs['raise_on_warnings'] = settings.DEBUG kwargs['client_flags'] = [ # Need potentially affected rows on UPDATE mysql.connector.constants.ClientFlag.FOUND_ROWS, ] try: kwargs.update(settings_dict['OPTIONS']) except KeyError: # OPTIONS missing is OK pass return kwargs def get_new_connection(self, conn_params): # Django 1.6 cnx = mysql.connector.connect(**conn_params) self.server_version = cnx.get_server_version() cnx.set_converter_class(DjangoMySQLConverter) connection_created.send(sender=self.__class__, connection=self) return cnx def init_connection_state(self): # Django 1.6 if self.server_version < (5, 5, 3): # See sysvar_sql_auto_is_null in MySQL Reference manual self.connection.cmd_query("SET SQL_AUTO_IS_NULL = 0") if 'AUTOCOMMIT' in self.settings_dict: self.set_autocommit(self.settings_dict['AUTOCOMMIT']) def create_cursor(self): # Django 1.6 cursor = self.connection.cursor() return CursorWrapper(cursor) def _connect(self): """Setup the connection with MySQL""" self.connection = self.get_new_connection(self.get_connection_params()) self.init_connection_state() def _cursor(self): """Return a CursorWrapper object Returns a CursorWrapper """ try: # Django 1.6 return super(DatabaseWrapper, self)._cursor() except AttributeError: if not self.connection: self._connect() return self.create_cursor() def get_server_version(self): """Returns the MySQL server version of current connection Returns a tuple """ return self.server_version def disable_constraint_checking(self): """Disables foreign key checks 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. Returns True """ self.cursor().execute('SET @@session.foreign_key_checks = 0') return True def enable_constraint_checking(self): """Re-enable foreign key checks Re-enable foreign key checks after they have been disabled. """ # Override needs_rollback in case constraint_checks_disabled is # nested inside transaction.atomic. if django.VERSION >= (1, 6): self.needs_rollback, needs_rollback = False, self.needs_rollback try: self.cursor().execute('SET @@session.foreign_key_checks = 1') finally: if django.VERSION >= (1, 6): self.needs_rollback = needs_rollback def check_constraints(self, table_names=None): """Check rows in tables for invalid foreign key references 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") """ ref_query = """ SELECT REFERRING.`{0}`, REFERRING.`{1}` FROM `{2}` as REFERRING LEFT JOIN `{3}` as REFERRED ON (REFERRING.`{4}` = REFERRED.`{5}`) WHERE REFERRING.`{6}` IS NOT NULL AND REFERRED.`{7}` IS NULL""" 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(ref_query.format(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(): msg = ("The row in table '{0}' with primary key '{1}' has " "an invalid foreign key: {2}.{3} contains a value " "'{4}' that does not have a corresponding value in " "{5}.{6}.".format(table_name, bad_row[0], table_name, column_name, bad_row[1], referenced_table_name, referenced_column_name)) raise utils.IntegrityError(msg) def _rollback(self): try: BaseDatabaseWrapper._rollback(self) except NotSupportedError: pass def _set_autocommit(self, autocommit): # Django 1.6 self.connection.autocommit = autocommit def is_usable(self): # Django 1.6 return self.connection.is_connected()
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. # Moved from DatabaseCreation class in Django v1.8 _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({}, '\\', '\\\\')," r" '%%', '\%%'), '_', '\_')") pattern_ops = { 'contains': "LIKE BINARY CONCAT('%%', {}, '%%')", 'icontains': "LIKE CONCAT('%%', {}, '%%')", 'startswith': "LIKE BINARY CONCAT({}, '%%')", 'istartswith': "LIKE CONCAT({}, '%%')", 'endswith': "LIKE BINARY CONCAT('%%', {})", 'iendswith': "LIKE CONCAT('%%', {})", } SchemaEditorClass = DatabaseSchemaEditor Database = mysql.connector def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) try: self._use_pure = self.settings_dict['OPTIONS']['use_pure'] except KeyError: self._use_pure = True if not self.use_pure: self.converter = DjangoCMySQLConverter() else: self.converter = DjangoMySQLConverter() self.ops = DatabaseOperations(self) self.features = DatabaseFeatures(self) self.client = DatabaseClient(self) self.creation = DatabaseCreation(self) self.introspection = DatabaseIntrospection(self) self.validation = DatabaseValidation(self) def _valid_connection(self): if self.connection: return self.connection.is_connected() return False def get_connection_params(self): # Django 1.6 kwargs = { 'charset': 'utf8', 'use_unicode': True, 'buffered': False, 'consume_results': True, } settings_dict = self.settings_dict if settings_dict['USER']: kwargs['user'] = settings_dict['USER'] if settings_dict['NAME']: kwargs['database'] = settings_dict['NAME'] if settings_dict['PASSWORD']: kwargs['passwd'] = 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']) # Raise exceptions for database warnings if DEBUG is on kwargs['raise_on_warnings'] = settings.DEBUG kwargs['client_flags'] = [ # Need potentially affected rows on UPDATE mysql.connector.constants.ClientFlag.FOUND_ROWS, ] try: kwargs.update(settings_dict['OPTIONS']) except KeyError: # OPTIONS missing is OK pass return kwargs def get_new_connection(self, conn_params): # Django 1.6 if not self.use_pure: conn_params['converter_class'] = DjangoCMySQLConverter else: conn_params['converter_class'] = DjangoMySQLConverter cnx = mysql.connector.connect(**conn_params) return cnx def init_connection_state(self): # Django 1.6 if self.mysql_version < (5, 5, 3): # See sysvar_sql_auto_is_null in MySQL Reference manual self.connection.cmd_query("SET SQL_AUTO_IS_NULL = 0") if 'AUTOCOMMIT' in self.settings_dict: try: # Django 1.6 self.set_autocommit(self.settings_dict['AUTOCOMMIT']) except AttributeError: self._set_autocommit(self.settings_dict['AUTOCOMMIT']) def create_cursor(self): # Django 1.6 cursor = self.connection.cursor() return CursorWrapper(cursor) def _connect(self): """Setup the connection with MySQL""" self.connection = self.get_new_connection(self.get_connection_params()) connection_created.send(sender=self.__class__, connection=self) self.init_connection_state() def _cursor(self): """Return a CursorWrapper object Returns a CursorWrapper """ try: # Django 1.6 return super(DatabaseWrapper, self)._cursor() except AttributeError: if not self.connection: self._connect() return self.create_cursor() def get_server_version(self): """Returns the MySQL server version of current connection Returns a tuple """ try: # Django 1.6 self.ensure_connection() except AttributeError: if not self.connection: self._connect() return self.connection.get_server_version() def disable_constraint_checking(self): """Disables foreign key checks 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. Returns True """ self.cursor().execute('SET @@session.foreign_key_checks = 0') return True def enable_constraint_checking(self): """Re-enable foreign key checks Re-enable foreign key checks after they have been disabled. """ # Override needs_rollback in case constraint_checks_disabled is # nested inside transaction.atomic. if django.VERSION >= (1, 6): self.needs_rollback, needs_rollback = False, self.needs_rollback try: self.cursor().execute('SET @@session.foreign_key_checks = 1') finally: if django.VERSION >= (1, 6): self.needs_rollback = needs_rollback def check_constraints(self, table_names=None): """Check rows in tables for invalid foreign key references 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") """ ref_query = """ SELECT REFERRING.`{0}`, REFERRING.`{1}` FROM `{2}` as REFERRING LEFT JOIN `{3}` as REFERRED ON (REFERRING.`{4}` = REFERRED.`{5}`) WHERE REFERRING.`{6}` IS NOT NULL AND REFERRED.`{7}` IS NULL""" 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(ref_query.format(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(): msg = ("The row in table '{0}' with primary key '{1}' has " "an invalid foreign key: {2}.{3} contains a value " "'{4}' that does not have a corresponding value in " "{5}.{6}.".format(table_name, bad_row[0], table_name, column_name, bad_row[1], referenced_table_name, referenced_column_name)) raise utils.IntegrityError(msg) def _rollback(self): try: BaseDatabaseWrapper._rollback(self) except NotSupportedError: pass def _set_autocommit(self, autocommit): # Django 1.6 with self.wrap_database_errors: self.connection.autocommit = autocommit def schema_editor(self, *args, **kwargs): """Returns a new instance of this backend's SchemaEditor""" # Django 1.7 return DatabaseSchemaEditor(self, *args, **kwargs) def is_usable(self): # Django 1.6 return self.connection.is_connected() @cached_property def mysql_version(self): config = self.get_connection_params() temp_conn = mysql.connector.connect(**config) server_version = temp_conn.get_server_version() temp_conn.close() return server_version @property def use_pure(self): return not HAVE_CEXT or self._use_pure
class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'mysql' 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', } def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.server_version = None # Since some features depend on the MySQL version, we need to connect self._connect() 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 _valid_connection(self): if self.connection: return self.connection.is_connected() return False def _connect(self): """Setup the connection with MySQL""" kwargs = { 'charset': 'utf8', 'use_unicode': True, 'sql_mode': 'TRADITIONAL', 'buffered': True, } settings_dict = self.settings_dict if settings_dict['USER']: kwargs['user'] = settings_dict['USER'] if settings_dict['NAME']: kwargs['database'] = settings_dict['NAME'] if settings_dict['PASSWORD']: kwargs['passwd'] = 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']) kwargs['client_flags'] = [ # Need potentially affected rows on UPDATE mysql.connector.constants.ClientFlag.FOUND_ROWS, ] kwargs.update(settings_dict['OPTIONS']) self.connection = mysql.connector.connect(**kwargs) self.server_version = self.connection.get_server_version() self.connection.set_converter_class(DjangoMySQLConverter) connection_created.send(sender=self.__class__, connection=self) if self.server_version < (5, 5, 3): # http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_sql_auto_is_null self.connection.cmd_query("SET SQL_AUTO_IS_NULL = 0") def _cursor(self): """Return a CursorWrapper object Returns a CursorWrapper """ if not self._valid_connection(): self._connect() return CursorWrapper(self.connection.cursor()) def get_server_version(self): """Returns the MySQL server version of current connection Returns a tuple """ return self.server_version def disable_constraint_checking(self): """Disables foreign key checks 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. Returns True """ self.cursor().execute('SET @@session.foreign_key_checks = 0') return True def enable_constraint_checking(self): """Re-enable foreign key checks Re-enable foreign key checks after they have been disabled. """ self.cursor().execute('SET @@session.foreign_key_checks = 1') def check_constraints(self, table_names=None): """Check rows in tables for invalid foreign key references 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") """ ref_query = """ SELECT REFERRING.`{0}`, REFERRING.`{1}` FROM `{2}` as REFERRING LEFT JOIN `{3}` as REFERRED ON (REFERRING.`{4}` = REFERRED.`{5}`) WHERE REFERRING.`{6}` IS NOT NULL AND REFERRED.`{7}` IS NULL""" 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(ref_query.format(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(): msg = ("The row in table '{0}' with primary key '{1}' has " "an invalid foreign key: {2}.{3} contains a value " "'{4}' that does not have a corresponding value in " "{5}.{6}.".format(table_name, bad_row[0], table_name, column_name, bad_row[1], referenced_table_name, referenced_column_name)) raise utils.IntegrityError(msg)
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. # Moved from DatabaseCreation class in Django v1.8 _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({}, '\\', '\\\\')," r" '%%', '\%%'), '_', '\_')") pattern_ops = { 'contains': "LIKE BINARY CONCAT('%%', {}, '%%')", 'icontains': "LIKE CONCAT('%%', {}, '%%')", 'startswith': "LIKE BINARY CONCAT({}, '%%')", 'istartswith': "LIKE CONCAT({}, '%%')", 'endswith': "LIKE BINARY CONCAT('%%', {})", 'iendswith': "LIKE CONCAT('%%', {})", } SchemaEditorClass = DatabaseSchemaEditor Database = mysql.connector def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) try: self._use_pure = self.settings_dict['OPTIONS']['use_pure'] except KeyError: self._use_pure = True if not self.use_pure: self.converter = DjangoCMySQLConverter() else: self.converter = DjangoMySQLConverter() self.ops = DatabaseOperations(self) self.features = DatabaseFeatures(self) self.client = DatabaseClient(self) self.creation = DatabaseCreation(self) self.introspection = DatabaseIntrospection(self) self.validation = DatabaseValidation(self) def _valid_connection(self): if self.connection: return self.connection.is_connected() return False def get_connection_params(self): # Django 1.6 kwargs = { 'charset': 'utf8', 'use_unicode': True, 'buffered': False, 'consume_results': True, } settings_dict = self.settings_dict if settings_dict['USER']: kwargs['user'] = settings_dict['USER'] if settings_dict['NAME']: kwargs['database'] = settings_dict['NAME'] if settings_dict['PASSWORD']: kwargs['passwd'] = 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']) # Raise exceptions for database warnings if DEBUG is on kwargs['raise_on_warnings'] = settings.DEBUG kwargs['client_flags'] = [ # Need potentially affected rows on UPDATE mysql.connector.constants.ClientFlag.FOUND_ROWS, ] try: kwargs.update(settings_dict['OPTIONS']) except KeyError: # OPTIONS missing is OK pass return kwargs def get_new_connection(self, conn_params): # Django 1.6 if not self.use_pure: conn_params['converter_class'] = DjangoCMySQLConverter else: conn_params['converter_class'] = DjangoMySQLConverter cnx = mysql.connector.connect(**conn_params) return cnx def init_connection_state(self): # Django 1.6 if self.mysql_version < (5, 5, 3): # See sysvar_sql_auto_is_null in MySQL Reference manual self.connection.cmd_query("SET SQL_AUTO_IS_NULL = 0") if 'AUTOCOMMIT' in self.settings_dict: try: # Django 1.6 self.set_autocommit(self.settings_dict['AUTOCOMMIT']) except AttributeError: self._set_autocommit(self.settings_dict['AUTOCOMMIT']) def create_cursor(self): # Django 1.6 cursor = self.connection.cursor() return CursorWrapper(cursor) def _connect(self): """Setup the connection with MySQL""" self.connection = self.get_new_connection(self.get_connection_params()) connection_created.send(sender=self.__class__, connection=self) self.init_connection_state() def _cursor(self): """Return a CursorWrapper object Returns a CursorWrapper """ try: # Django 1.6 return super(DatabaseWrapper, self)._cursor() except AttributeError: if not self.connection: self._connect() return self.create_cursor() def get_server_version(self): """Returns the MySQL server version of current connection Returns a tuple """ try: # Django 1.6 self.ensure_connection() except AttributeError: if not self.connection: self._connect() return self.connection.get_server_version() def disable_constraint_checking(self): """Disables foreign key checks 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. Returns True """ self.cursor().execute('SET @@session.foreign_key_checks = 0') return True def enable_constraint_checking(self): """Re-enable foreign key checks Re-enable foreign key checks after they have been disabled. """ # Override needs_rollback in case constraint_checks_disabled is # nested inside transaction.atomic. if django.VERSION >= (1, 6): self.needs_rollback, needs_rollback = False, self.needs_rollback try: self.cursor().execute('SET @@session.foreign_key_checks = 1') finally: if django.VERSION >= (1, 6): self.needs_rollback = needs_rollback def check_constraints(self, table_names=None): """Check rows in tables for invalid foreign key references 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") """ ref_query = """ SELECT REFERRING.`{0}`, REFERRING.`{1}` FROM `{2}` as REFERRING LEFT JOIN `{3}` as REFERRED ON (REFERRING.`{4}` = REFERRED.`{5}`) WHERE REFERRING.`{6}` IS NOT NULL AND REFERRED.`{7}` IS NULL""" 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( ref_query.format(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(): msg = ("The row in table '{0}' with primary key '{1}' has " "an invalid foreign key: {2}.{3} contains a value " "'{4}' that does not have a corresponding value in " "{5}.{6}.".format(table_name, bad_row[0], table_name, column_name, bad_row[1], referenced_table_name, referenced_column_name)) raise utils.IntegrityError(msg) def _rollback(self): try: BaseDatabaseWrapper._rollback(self) except NotSupportedError: pass def _set_autocommit(self, autocommit): # Django 1.6 with self.wrap_database_errors: self.connection.autocommit = autocommit def schema_editor(self, *args, **kwargs): """Returns a new instance of this backend's SchemaEditor""" # Django 1.7 return DatabaseSchemaEditor(self, *args, **kwargs) def is_usable(self): # Django 1.6 return self.connection.is_connected() @cached_property def mysql_version(self): config = self.get_connection_params() temp_conn = mysql.connector.connect(**config) server_version = temp_conn.get_server_version() temp_conn.close() return server_version @property def use_pure(self): return not HAVE_CEXT or self._use_pure
class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'mysql' 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', } def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.server_version = None # Since some features depend on the MySQL version, we need to connect self._connect() 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 _valid_connection(self): if self.connection: return self.connection.is_connected() return False def _connect(self): """Setup the connection with MySQL""" kwargs = { 'charset': 'utf8', 'use_unicode': True, 'sql_mode': 'TRADITIONAL', 'buffered': True, } settings_dict = self.settings_dict if settings_dict['USER']: kwargs['user'] = settings_dict['USER'] if settings_dict['NAME']: kwargs['database'] = settings_dict['NAME'] if settings_dict['PASSWORD']: kwargs['passwd'] = 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']) kwargs['client_flags'] = [ # Need potentially affected rows on UPDATE mysql.connector.constants.ClientFlag.FOUND_ROWS, ] kwargs.update(settings_dict['OPTIONS']) self.connection = mysql.connector.connect(**kwargs) self.server_version = self.connection.get_server_version() self.connection.set_converter_class(DjangoMySQLConverter) connection_created.send(sender=self.__class__, connection=self) if self.server_version < (5, 5, 3): # http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_sql_auto_is_null self.connection.cmd_query("SET SQL_AUTO_IS_NULL = 0") def _cursor(self): """Return a CursorWrapper object Returns a CursorWrapper """ if not self._valid_connection(): self._connect() return CursorWrapper(self.connection.cursor()) def get_server_version(self): """Returns the MySQL server version of current connection Returns a tuple """ return self.server_version def disable_constraint_checking(self): """Disables foreign key checks 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. Returns True """ self.cursor().execute('SET @@session.foreign_key_checks = 0') return True def enable_constraint_checking(self): """Re-enable foreign key checks Re-enable foreign key checks after they have been disabled. """ self.cursor().execute('SET @@session.foreign_key_checks = 1') def check_constraints(self, table_names=None): """Check rows in tables for invalid foreign key references 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") """ ref_query = """ SELECT REFERRING.`{0}`, REFERRING.`{1}` FROM `{2}` as REFERRING LEFT JOIN `{3}` as REFERRED ON (REFERRING.`{4}` = REFERRED.`{5}`) WHERE REFERRING.`{6}` IS NOT NULL AND REFERRED.`{7}` IS NULL""" 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( ref_query.format(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(): msg = ("The row in table '{0}' with primary key '{1}' has " "an invalid foreign key: {2}.{3} contains a value " "'{4}' that does not have a corresponding value in " "{5}.{6}.".format(table_name, bad_row[0], table_name, column_name, bad_row[1], referenced_table_name, referenced_column_name)) raise utils.IntegrityError(msg)