Commit 6bf79640 authored by Aaron Elliot Ross's avatar Aaron Elliot Ross Committed by Tim Graham
Browse files

Fixed #26171 -- Made MySQL create an index on ForeignKeys with db_contraint=False.

Refactored "Prevented unneeded index creation on MySQL-InnoDB" (2ceb10f3)
to avoid setting db_index=False.
parent 5fe1c922
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -884,7 +884,7 @@ class BaseDatabaseSchemaEditor(object):
            return []
        output = []
        for field in model._meta.local_fields:
            if field.db_index and not field.unique:
            if self._field_should_be_indexed(model, field):
                output.append(self._create_index_sql(model, [field], suffix=""))

        for field_names in model._meta.index_together:
@@ -892,6 +892,9 @@ class BaseDatabaseSchemaEditor(object):
            output.append(self._create_index_sql(model, fields, suffix="_idx"))
        return output

    def _field_should_be_indexed(self, model, field):
        return field.db_index and not field.unique

    def _rename_field_sql(self, table, old_field, new_field, new_type):
        return self.sql_rename_column % {
            "table": self.quote_name(table),
+11 −8
Original line number Diff line number Diff line
@@ -51,17 +51,20 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                'column': self.quote_name(field.column),
            }, [effective_default])

    def _model_indexes_sql(self, model):
    def _field_should_be_indexed(self, model, field):
        create_index = super(DatabaseSchemaEditor, self)._field_should_be_indexed(model, field)
        storage = self.connection.introspection.get_storage_engine(
            self.connection.cursor(), model._meta.db_table
        )
        if storage == "InnoDB":
            for field in model._meta.local_fields:
                if field.db_index and not field.unique and field.get_internal_type() == "ForeignKey":
                    # Temporary setting db_index to False (in memory) to disable
                    # index creation for FKs (index automatically created by MySQL)
                    field.db_index = False
        return super(DatabaseSchemaEditor, self)._model_indexes_sql(model)
        # No need to create an index for ForeignKey fields except if
        # db_constraint=False because the index from that constraint won't be
        # created.
        if (storage == "InnoDB" and
                create_index and
                field.get_internal_type() == 'ForeignKey' and
                field.db_constraint):
            return False
        return create_index

    def _delete_composed_index(self, model, fields, *args):
        """
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ class CurrentTranslation(models.ForeignObject):
class ArticleTranslation(models.Model):

    article = models.ForeignKey('indexes.Article', models.CASCADE)
    article_no_constraint = models.ForeignKey('indexes.Article', models.CASCADE, db_constraint=False)
    language = models.CharField(max_length=10, unique=True)
    content = models.TextField()

+5 −2
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ class SchemaIndexesTests(TestCase):
    def test_no_index_for_foreignkey(self):
        """
        MySQL on InnoDB already creates indexes automatically for foreign keys.
        (#14180).
        (#14180). An index should be created if db_constraint=False (#26171).
        """
        storage = connection.introspection.get_storage_engine(
            connection.cursor(), ArticleTranslation._meta.db_table
@@ -70,4 +70,7 @@ class SchemaIndexesTests(TestCase):
        if storage != "InnoDB":
            self.skip("This test only applies to the InnoDB storage engine")
        index_sql = connection.schema_editor()._model_indexes_sql(ArticleTranslation)
        self.assertEqual(index_sql, [])
        self.assertEqual(index_sql, [
            'CREATE INDEX `indexes_articletranslation_99fb53c2` '
            'ON `indexes_articletranslation` (`article_no_constraint_id`)'
        ])