diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py index 67c34f028f..e3677dd35a 100644 --- a/django/db/backends/base/features.py +++ b/django/db/backends/base/features.py @@ -163,9 +163,6 @@ class BaseDatabaseFeatures(object): # Can the backend introspect the column order (ASC/DESC) for indexes? supports_index_column_ordering = True - # Can the backend introspect the type of index created? - can_introspect_index_type = False - # Support for the DISTINCT ON clause can_distinct_on_fields = False diff --git a/django/db/backends/base/introspection.py b/django/db/backends/base/introspection.py index b3eec72469..7da14985c3 100644 --- a/django/db/backends/base/introspection.py +++ b/django/db/backends/base/introspection.py @@ -172,6 +172,8 @@ class BaseDatabaseIntrospection(object): * foreign_key: (table, column) of target, or None * check: True if check constraint, False otherwise * index: True if index, False otherwise. + * orders: The order (ASC/DESC) defined for the columns of indexes + * type: The type of the index (btree, hash, etc.) Some backends may return special constraint names that don't exist if they don't name constraints of a certain type (e.g. SQLite) diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py index 4ac8864ba6..3a8b7f6933 100644 --- a/django/db/backends/mysql/introspection.py +++ b/django/db/backends/mysql/introspection.py @@ -201,17 +201,17 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): constraints[constraint]['unique'] = True # Now add in the indexes cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name)) - for table, non_unique, index, colseq, column in [x[:5] for x in cursor.fetchall()]: + for table, non_unique, index, colseq, column, type_ in [x[:5] + (x[10],) for x in cursor.fetchall()]: if index not in constraints: constraints[index] = { 'columns': OrderedSet(), 'primary_key': False, 'unique': False, - 'index': True, 'check': False, 'foreign_key': None, } constraints[index]['index'] = True + constraints[index]['type'] = type_.lower() constraints[index]['columns'].add(column) # Convert the sorted sets to lists for constraint in constraints.values(): diff --git a/django/db/backends/oracle/introspection.py b/django/db/backends/oracle/introspection.py index f8eec17256..5c0c5c5861 100644 --- a/django/db/backends/oracle/introspection.py +++ b/django/db/backends/oracle/introspection.py @@ -258,20 +258,20 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): # Now get indexes cursor.execute(""" SELECT - index_name, - LOWER(column_name), descend + cols.index_name, LOWER(cols.column_name), cols.descend, + LOWER(ind.index_type) FROM - user_ind_columns cols + user_ind_columns cols, user_indexes ind WHERE - table_name = UPPER(%s) AND + cols.table_name = UPPER(%s) AND NOT EXISTS ( SELECT 1 FROM user_constraints cons WHERE cols.index_name = cons.index_name - ) + ) AND cols.index_name = ind.index_name ORDER BY cols.column_position """, [table_name]) - for constraint, column, order in cursor.fetchall(): + for constraint, column, order, type_ in cursor.fetchall(): # If we're the first column, make the record if constraint not in constraints: constraints[constraint] = { @@ -282,6 +282,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): "foreign_key": None, "check": False, "index": True, + "type": 'btree' if type_ == 'normal' else type_, } # Record the details constraints[constraint]['columns'].append(column) diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index 918dfe7978..a9e1c77480 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -22,7 +22,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): can_introspect_autofield = True can_introspect_ip_address_field = True can_introspect_small_integer_field = True - can_introspect_index_type = True can_distinct_on_fields = True can_rollback_ddl = True supports_combined_alters = True diff --git a/django/db/backends/sqlite3/introspection.py b/django/db/backends/sqlite3/introspection.py index 32efd7ff6f..db475505d8 100644 --- a/django/db/backends/sqlite3/introspection.py +++ b/django/db/backends/sqlite3/introspection.py @@ -255,8 +255,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): "index": True, } constraints[index]['columns'].append(column) - # Add column orders for indexes + # Add type and column orders for indexes if constraints[index]['index'] and not constraints[index]['unique']: + # SQLite doesn't support any index type other than b-tree + constraints[index]['type'] = 'btree' cursor.execute( "SELECT sql FROM sqlite_master " "WHERE type='index' AND name=%s" % self.connection.ops.quote_name(index) diff --git a/tests/introspection/tests.py b/tests/introspection/tests.py index 766e51dea7..68d621d762 100644 --- a/tests/introspection/tests.py +++ b/tests/introspection/tests.py @@ -184,13 +184,14 @@ class IntrospectionTests(TransactionTestCase): self.assertNotIn('first_name', indexes) self.assertIn('id', indexes) - @skipUnlessDBFeature('can_introspect_index_type') def test_get_constraints_index_types(self): with connection.cursor() as cursor: constraints = connection.introspection.get_constraints(cursor, Article._meta.db_table) + index = {} for key, val in constraints.items(): - if val['index'] and not (val['primary_key'] or val['unique']): - self.assertEqual(val['type'], 'btree') + if val['columns'] == ['headline', 'pub_date']: + index = val + self.assertEqual(index['type'], 'btree') @skipUnlessDBFeature('supports_index_column_ordering') def test_get_constraints_indexes_orders(self): diff --git a/tests/postgres_tests/test_indexes.py b/tests/postgres_tests/test_indexes.py index 8c15f01d47..235d72c532 100644 --- a/tests/postgres_tests/test_indexes.py +++ b/tests/postgres_tests/test_indexes.py @@ -44,13 +44,14 @@ class SchemaTests(PostgreSQLTestCase): # Ensure the table is there and doesn't have an index. self.assertNotIn('field', self.get_constraints(IntegerArrayModel._meta.db_table)) # Add the index - index = GinIndex(fields=['field'], name='integer_array_model_field_gin') + index_name = 'integer_array_model_field_gin' + index = GinIndex(fields=['field'], name=index_name) with connection.schema_editor() as editor: editor.add_index(IntegerArrayModel, index) - self.assertIn('integer_array_model_field_gin', self.get_constraints(IntegerArrayModel._meta.db_table)) constraints = self.get_constraints(IntegerArrayModel._meta.db_table) - self.assertEqual(constraints['integer_array_model_field_gin']['type'], 'gin') + # Check gin index was added + self.assertEqual(constraints[index_name]['type'], 'gin') # Drop the index with connection.schema_editor() as editor: editor.remove_index(IntegerArrayModel, index) - self.assertNotIn('integer_array_model_field_gin', self.get_constraints(IntegerArrayModel._meta.db_table)) + self.assertNotIn(index_name, self.get_constraints(IntegerArrayModel._meta.db_table))