Fixed #27097 -- Added index type introspection to built-in db backends.

This commit is contained in:
Akshesh 2016-08-25 12:42:17 +05:30 committed by Tim Graham
parent 082f5bfdbc
commit dbccf163b6
8 changed files with 23 additions and 20 deletions

View File

@ -163,9 +163,6 @@ class BaseDatabaseFeatures(object):
# Can the backend introspect the column order (ASC/DESC) for indexes? # Can the backend introspect the column order (ASC/DESC) for indexes?
supports_index_column_ordering = True 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 # Support for the DISTINCT ON clause
can_distinct_on_fields = False can_distinct_on_fields = False

View File

@ -172,6 +172,8 @@ class BaseDatabaseIntrospection(object):
* foreign_key: (table, column) of target, or None * foreign_key: (table, column) of target, or None
* check: True if check constraint, False otherwise * check: True if check constraint, False otherwise
* index: True if index, 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 Some backends may return special constraint names that don't exist
if they don't name constraints of a certain type (e.g. SQLite) if they don't name constraints of a certain type (e.g. SQLite)

View File

@ -201,17 +201,17 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
constraints[constraint]['unique'] = True constraints[constraint]['unique'] = True
# Now add in the indexes # Now add in the indexes
cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name)) 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: if index not in constraints:
constraints[index] = { constraints[index] = {
'columns': OrderedSet(), 'columns': OrderedSet(),
'primary_key': False, 'primary_key': False,
'unique': False, 'unique': False,
'index': True,
'check': False, 'check': False,
'foreign_key': None, 'foreign_key': None,
} }
constraints[index]['index'] = True constraints[index]['index'] = True
constraints[index]['type'] = type_.lower()
constraints[index]['columns'].add(column) constraints[index]['columns'].add(column)
# Convert the sorted sets to lists # Convert the sorted sets to lists
for constraint in constraints.values(): for constraint in constraints.values():

View File

@ -258,20 +258,20 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
# Now get indexes # Now get indexes
cursor.execute(""" cursor.execute("""
SELECT SELECT
index_name, cols.index_name, LOWER(cols.column_name), cols.descend,
LOWER(column_name), descend LOWER(ind.index_type)
FROM FROM
user_ind_columns cols user_ind_columns cols, user_indexes ind
WHERE WHERE
table_name = UPPER(%s) AND cols.table_name = UPPER(%s) AND
NOT EXISTS ( NOT EXISTS (
SELECT 1 SELECT 1
FROM user_constraints cons FROM user_constraints cons
WHERE cols.index_name = cons.index_name WHERE cols.index_name = cons.index_name
) ) AND cols.index_name = ind.index_name
ORDER BY cols.column_position ORDER BY cols.column_position
""", [table_name]) """, [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 we're the first column, make the record
if constraint not in constraints: if constraint not in constraints:
constraints[constraint] = { constraints[constraint] = {
@ -282,6 +282,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
"foreign_key": None, "foreign_key": None,
"check": False, "check": False,
"index": True, "index": True,
"type": 'btree' if type_ == 'normal' else type_,
} }
# Record the details # Record the details
constraints[constraint]['columns'].append(column) constraints[constraint]['columns'].append(column)

View File

@ -22,7 +22,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
can_introspect_autofield = True can_introspect_autofield = True
can_introspect_ip_address_field = True can_introspect_ip_address_field = True
can_introspect_small_integer_field = True can_introspect_small_integer_field = True
can_introspect_index_type = True
can_distinct_on_fields = True can_distinct_on_fields = True
can_rollback_ddl = True can_rollback_ddl = True
supports_combined_alters = True supports_combined_alters = True

View File

@ -255,8 +255,10 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
"index": True, "index": True,
} }
constraints[index]['columns'].append(column) 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']: 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( cursor.execute(
"SELECT sql FROM sqlite_master " "SELECT sql FROM sqlite_master "
"WHERE type='index' AND name=%s" % self.connection.ops.quote_name(index) "WHERE type='index' AND name=%s" % self.connection.ops.quote_name(index)

View File

@ -184,13 +184,14 @@ class IntrospectionTests(TransactionTestCase):
self.assertNotIn('first_name', indexes) self.assertNotIn('first_name', indexes)
self.assertIn('id', indexes) self.assertIn('id', indexes)
@skipUnlessDBFeature('can_introspect_index_type')
def test_get_constraints_index_types(self): def test_get_constraints_index_types(self):
with connection.cursor() as cursor: with connection.cursor() as cursor:
constraints = connection.introspection.get_constraints(cursor, Article._meta.db_table) constraints = connection.introspection.get_constraints(cursor, Article._meta.db_table)
index = {}
for key, val in constraints.items(): for key, val in constraints.items():
if val['index'] and not (val['primary_key'] or val['unique']): if val['columns'] == ['headline', 'pub_date']:
self.assertEqual(val['type'], 'btree') index = val
self.assertEqual(index['type'], 'btree')
@skipUnlessDBFeature('supports_index_column_ordering') @skipUnlessDBFeature('supports_index_column_ordering')
def test_get_constraints_indexes_orders(self): def test_get_constraints_indexes_orders(self):

View File

@ -44,13 +44,14 @@ class SchemaTests(PostgreSQLTestCase):
# Ensure the table is there and doesn't have an index. # Ensure the table is there and doesn't have an index.
self.assertNotIn('field', self.get_constraints(IntegerArrayModel._meta.db_table)) self.assertNotIn('field', self.get_constraints(IntegerArrayModel._meta.db_table))
# Add the index # 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: with connection.schema_editor() as editor:
editor.add_index(IntegerArrayModel, index) 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) 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 # Drop the index
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.remove_index(IntegerArrayModel, index) 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))