Fixed #27097 -- Added index type introspection to built-in db backends.
This commit is contained in:
parent
082f5bfdbc
commit
dbccf163b6
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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():
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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))
|
||||||
|
|
Loading…
Reference in New Issue